<commit_before>// This file is a part of the OpenSurgSim project.
// Copyright 2012-2013, SimQuest Solutions Inc.
//
// 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
/// Render Tests for the OsgBoxRepresentation class.

#include "SurgSim/Framework/Runtime.h"
#include "SurgSim/Framework/Scene.h"
#include "SurgSim/Framework/SceneElement.h"
#include "SurgSim/Graphics/OsgAxesRepresentation.h"
#include "SurgSim/Graphics/OsgBoxRepresentation.h"
#include "SurgSim/Graphics/OsgCapsuleRepresentation.h"
#include "SurgSim/Graphics/OsgCylinderRepresentation.h"
#include "SurgSim/Graphics/OsgManager.h"
#include "SurgSim/Graphics/OsgSphereRepresentation.h"
#include "SurgSim/Graphics/OsgSceneryRepresentation.h"
#include "SurgSim/Graphics/OsgViewElement.h"
#include "SurgSim/Graphics/RenderTests/RenderTest.h"
#include "SurgSim/Math/Quaternion.h"
#include "SurgSim/Math/Vector.h"

#include <gtest/gtest.h>

#include <random>

#include <osgUtil/SmoothingVisitor>

using SurgSim::Framework::Runtime;
using SurgSim::Framework::Scene;
using SurgSim::Framework::SceneElement;
using SurgSim::Math::Quaterniond;
using SurgSim::Math::RigidTransform3d;
using SurgSim::Math::Vector3d;
using SurgSim::Math::Vector2d;
using SurgSim::Math::makeRigidTransform;
using SurgSim::Math::makeRotationQuaternion;

namespace SurgSim
{

namespace Graphics
{

struct OsgRepresentationRenderTests : public RenderTest
{

};

/// This test will put all shape one by one along the X-axis
/// To make sure all shapes are aligned.
/// X-axis points horizontally to the right
/// Y-axis points vertically up
/// Z-axis is perpendicular to the screen and points out
TEST_F(OsgRepresentationRenderTests, RepresentationTest)
{
	///	Box position
	Vector3d boxPosition(0.05, 0.0, -0.2);
	/// Capsule position
	Vector3d capsulePosition(-0.05, 0.0, -0.2);
	/// Cylinder position
	Vector3d cylinderPosition(-0.025, 0.0, -0.2);
	/// Sphere position
	Vector3d spherePosition(0.025, 0.0, -0.2);
	/// Size of the box
	Vector3d boxSize(0.01, 0.015, 0.01);
	/// Size of the capsule (radius, height)
	Vector2d capsuleSize(0.005, 0.015);
	/// Size of the cylinder
	Vector2d cylinderSize(0.005, 0.015);
	/// Radius of the sphere
	double sphereRadius = 0.005;

	/// Add representations to the view element so we don't need to make another concrete scene element
	std::shared_ptr<BoxRepresentation> boxRepresentation =
		std::make_shared<OsgBoxRepresentation>("box representation");
	viewElement->addComponent(boxRepresentation);

	std::shared_ptr<CapsuleRepresentation> capsuleRepresentation =
		std::make_shared<OsgCapsuleRepresentation>("capsule representation");
	viewElement->addComponent(capsuleRepresentation);

	std::shared_ptr<CylinderRepresentation> cylinderRepresentation =
		std::make_shared<OsgCylinderRepresentation>("cylinder representation");
	viewElement->addComponent(cylinderRepresentation);

	std::shared_ptr<SphereRepresentation> sphereRepresentation =
		std::make_shared<OsgSphereRepresentation>("sphere representation");
	viewElement->addComponent(sphereRepresentation);

	std::shared_ptr<AxesRepresentation> axesRepresentation =
		std::make_shared<OsgAxesRepresentation>("axes");
	viewElement->addComponent(axesRepresentation);

	/// Run the thread
	runtime->start();

	boxRepresentation->setLocalPose(makeRigidTransform(Quaterniond::Identity(), boxPosition));
	capsuleRepresentation->setLocalPose(makeRigidTransform(Quaterniond::Identity(), capsulePosition));
	cylinderRepresentation->setLocalPose(makeRigidTransform(Quaterniond::Identity(), cylinderPosition));
	sphereRepresentation->setLocalPose(makeRigidTransform(Quaterniond::Identity(), spherePosition));
	axesRepresentation->setLocalPose(makeRigidTransform(Quaterniond::Identity(), Vector3d(0.0, 0.0, -0.2)));

	/// Set the size of box
	boxRepresentation->setSizeXYZ(boxSize.x(), boxSize.y(), boxSize.z());
	/// Set the size of capsule
	/// Capsule should use Y-axis as its axis
	capsuleRepresentation->setSize(capsuleSize.x(), capsuleSize.y());
	/// Set the size of cylinder
	/// Cylinder should use Y-axis as its axis
	cylinderRepresentation->setSize(cylinderSize.x(), cylinderSize.y());
	/// Set the size of sphere
	sphereRepresentation->setRadius(sphereRadius);

	axesRepresentation->setSize(0.01);

	boost::this_thread::sleep(boost::posix_time::milliseconds(1500));

	boxRepresentation->setDrawAsWireFrame(true);
	capsuleRepresentation->setDrawAsWireFrame(true);
	cylinderRepresentation->setDrawAsWireFrame(true);
	sphereRepresentation->setDrawAsWireFrame(true);

	boost::this_thread::sleep(boost::posix_time::milliseconds(1500));
}

class  LineGeometryVisitor : public osg::NodeVisitor
{
public :
	LineGeometryVisitor() :
		NodeVisitor(NodeVisitor::TRAVERSE_ALL_CHILDREN),
		m_normalsScale(0.1)
	{
	}

	virtual ~LineGeometryVisitor() {}

	void apply(osg::Node& node) // NOLINT
	{
		traverse(node);
	}

	void apply(osg::Geode& geode) // NOLINT
	{
		// Only deal with 1 geometry for now ...
		if (geode.getNumDrawables() > 0)
		{
			osg::Geometry* curGeom = geode.getDrawable(0)->asGeometry();
			if (curGeom)
			{
				osg::Vec3Array* vertices = dynamic_cast<osg::Vec3Array*>(curGeom->getVertexArray());

				auto normals = curGeom->getNormalArray();
				geode.addDrawable(buildGeometry(vertices, normals, osg::Vec4(0.0, 0.0, 1.0, 1.0)));

				auto tangents = curGeom->getVertexAttribArray(7);

				auto cast = dynamic_cast<osg::Vec4Array*>(tangents);
				ASSERT_NE(nullptr, cast);
				ASSERT_EQ(vertices->size(), cast->size());
				geode.addDrawable(buildGeometry(vertices, tangents, osg::Vec4(1.0, 0.0, 0.0, 1.0)));

				auto bitangents = curGeom->getVertexAttribArray(8);
				cast = dynamic_cast<osg::Vec4Array*>(bitangents);
				ASSERT_NE(nullptr, cast);
				ASSERT_EQ(vertices->size(), cast->size());
				geode.addDrawable(buildGeometry(vertices, bitangents, osg::Vec4(0.0, 1.0, 0.0, 1.0)));

			}
		}
	}

	osg::Geometry* buildGeometry(osg::Vec3Array* geomVertices, osg::Array* directions, osg::Vec4 color)
	{
		// create Geometry object to store all the vertices and lines primitive.
		osg::Geometry* linesGeom = new osg::Geometry();

		osg::Vec3Array* vertices = new osg::Vec3Array(geomVertices->size() * 2);
		osg::Vec3 direction;
		for (size_t i = 0; i < geomVertices->size(); ++i)
		{

			(*vertices)[i * 2] = (*geomVertices)[i];
			switch (directions->getType())
			{
				case osg::Array::Vec3ArrayType:
					direction = static_cast<const osg::Vec3Array&>(*directions)[i];
					break;
				case osg::Array::Vec4ArrayType:
					for (int j = 0; j < 3; ++j)
					{
						direction[j] = static_cast<const osg::Vec4Array&>(*directions)[i].ptr()[j];
					}
					break;
				default:
					SURGSIM_FAILURE() << "Unhandled Array type.";
			}
			(*vertices)[i * 2 + 1] = (*geomVertices)[i] + direction * m_normalsScale;
		}

		// pass the created vertex array to the points geometry object.
		linesGeom->setVertexArray(vertices);

		// set the colors as before, plus using the above
		osg::Vec4Array* colors = new osg::Vec4Array;
		colors->push_back(color);
		linesGeom->setColorArray(colors, osg::Array::BIND_OVERALL);

		// 	set the normal in the same way color.
		osg::Vec3Array* normals = new osg::Vec3Array;
		normals->push_back(osg::Vec3(0.0f, -1.0f, 0.0f));
		linesGeom->setNormalArray(normals, osg::Array::BIND_OVERALL);

		linesGeom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINES, 0, vertices->size()));

		osg::StateSet* state = linesGeom->getOrCreateStateSet();
		state->setMode(GL_LIGHTING, osg::StateAttribute::PROTECTED | osg::StateAttribute::OFF);

		return linesGeom;
	}

private:
	float m_normalsScale;
};

TEST_F(OsgRepresentationRenderTests, TangentTest)
{
	auto element = std::make_shared<Framework::BasicSceneElement>("sphere");
	auto graphics = std::make_shared<OsgSceneryRepresentation>("sphere");
	graphics->loadModel("OsgRepresentationRenderTests/sphere0_5.obj");
	//graphics->setDrawAsWireFrame(true);

	viewElement->enableManipulator(true);

	camera->setLocalPose(Math::makeRigidTransform(
							 Vector3d(0.0, 0.0, -4.0),
							 Vector3d(0.0, 0.0, 0.0),
							 Vector3d(0.0, 1.0, 0.0)));

	osg::ref_ptr<osg::Node> node = graphics->getOsgNode();
	// Generate normals
	osgUtil::SmoothingVisitor sv;
	node->accept(sv);

	graphics->setGenerateTangents(true);

	LineGeometryVisitor visitor;
	node->accept(visitor);

	element->addComponent(graphics);
	scene->addSceneElement(element);

	runtime->start();

	boost::this_thread::sleep(boost::posix_time::milliseconds(500));
	runtime->stop();
}

};  // namespace Graphics

};  // namespace SurgSim
<commit_msg>Fix TangentRenderTest<commit_after>// This file is a part of the OpenSurgSim project.
// Copyright 2012-2013, SimQuest Solutions Inc.
//
// 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
/// Render Tests for the OsgBoxRepresentation class.

#include "SurgSim/Framework/Runtime.h"
#include "SurgSim/Framework/Scene.h"
#include "SurgSim/Framework/SceneElement.h"
#include "SurgSim/Graphics/OsgAxesRepresentation.h"
#include "SurgSim/Graphics/OsgBoxRepresentation.h"
#include "SurgSim/Graphics/OsgCapsuleRepresentation.h"
#include "SurgSim/Graphics/OsgCylinderRepresentation.h"
#include "SurgSim/Graphics/OsgManager.h"
#include "SurgSim/Graphics/OsgSphereRepresentation.h"
#include "SurgSim/Graphics/OsgSceneryRepresentation.h"
#include "SurgSim/Graphics/OsgViewElement.h"
#include "SurgSim/Graphics/RenderTests/RenderTest.h"
#include "SurgSim/Math/Quaternion.h"
#include "SurgSim/Math/Vector.h"

#include <gtest/gtest.h>

#include <random>

#include <osgUtil/SmoothingVisitor>

using SurgSim::Framework::Runtime;
using SurgSim::Framework::Scene;
using SurgSim::Framework::SceneElement;
using SurgSim::Math::Quaterniond;
using SurgSim::Math::RigidTransform3d;
using SurgSim::Math::Vector3d;
using SurgSim::Math::Vector2d;
using SurgSim::Math::makeRigidTransform;
using SurgSim::Math::makeRotationQuaternion;

namespace SurgSim
{

namespace Graphics
{

struct OsgRepresentationRenderTests : public RenderTest
{

};

/// This test will put all shape one by one along the X-axis
/// To make sure all shapes are aligned.
/// X-axis points horizontally to the right
/// Y-axis points vertically up
/// Z-axis is perpendicular to the screen and points out
TEST_F(OsgRepresentationRenderTests, RepresentationTest)
{
	///	Box position
	Vector3d boxPosition(0.05, 0.0, -0.2);
	/// Capsule position
	Vector3d capsulePosition(-0.05, 0.0, -0.2);
	/// Cylinder position
	Vector3d cylinderPosition(-0.025, 0.0, -0.2);
	/// Sphere position
	Vector3d spherePosition(0.025, 0.0, -0.2);
	/// Size of the box
	Vector3d boxSize(0.01, 0.015, 0.01);
	/// Size of the capsule (radius, height)
	Vector2d capsuleSize(0.005, 0.015);
	/// Size of the cylinder
	Vector2d cylinderSize(0.005, 0.015);
	/// Radius of the sphere
	double sphereRadius = 0.005;

	/// Add representations to the view element so we don't need to make another concrete scene element
	std::shared_ptr<BoxRepresentation> boxRepresentation =
		std::make_shared<OsgBoxRepresentation>("box representation");
	viewElement->addComponent(boxRepresentation);

	std::shared_ptr<CapsuleRepresentation> capsuleRepresentation =
		std::make_shared<OsgCapsuleRepresentation>("capsule representation");
	viewElement->addComponent(capsuleRepresentation);

	std::shared_ptr<CylinderRepresentation> cylinderRepresentation =
		std::make_shared<OsgCylinderRepresentation>("cylinder representation");
	viewElement->addComponent(cylinderRepresentation);

	std::shared_ptr<SphereRepresentation> sphereRepresentation =
		std::make_shared<OsgSphereRepresentation>("sphere representation");
	viewElement->addComponent(sphereRepresentation);

	std::shared_ptr<AxesRepresentation> axesRepresentation =
		std::make_shared<OsgAxesRepresentation>("axes");
	viewElement->addComponent(axesRepresentation);

	/// Run the thread
	runtime->start();

	boxRepresentation->setLocalPose(makeRigidTransform(Quaterniond::Identity(), boxPosition));
	capsuleRepresentation->setLocalPose(makeRigidTransform(Quaterniond::Identity(), capsulePosition));
	cylinderRepresentation->setLocalPose(makeRigidTransform(Quaterniond::Identity(), cylinderPosition));
	sphereRepresentation->setLocalPose(makeRigidTransform(Quaterniond::Identity(), spherePosition));
	axesRepresentation->setLocalPose(makeRigidTransform(Quaterniond::Identity(), Vector3d(0.0, 0.0, -0.2)));

	/// Set the size of box
	boxRepresentation->setSizeXYZ(boxSize.x(), boxSize.y(), boxSize.z());
	/// Set the size of capsule
	/// Capsule should use Y-axis as its axis
	capsuleRepresentation->setSize(capsuleSize.x(), capsuleSize.y());
	/// Set the size of cylinder
	/// Cylinder should use Y-axis as its axis
	cylinderRepresentation->setSize(cylinderSize.x(), cylinderSize.y());
	/// Set the size of sphere
	sphereRepresentation->setRadius(sphereRadius);

	axesRepresentation->setSize(0.01);

	boost::this_thread::sleep(boost::posix_time::milliseconds(1500));

	boxRepresentation->setDrawAsWireFrame(true);
	capsuleRepresentation->setDrawAsWireFrame(true);
	cylinderRepresentation->setDrawAsWireFrame(true);
	sphereRepresentation->setDrawAsWireFrame(true);

	boost::this_thread::sleep(boost::posix_time::milliseconds(1500));
}

class  LineGeometryVisitor : public osg::NodeVisitor
{
public :
	LineGeometryVisitor() :
		NodeVisitor(NodeVisitor::TRAVERSE_ALL_CHILDREN),
		m_normalsScale(0.1)
	{
	}

	virtual ~LineGeometryVisitor() {}

	void apply(osg::Node& node) // NOLINT
	{
		traverse(node);
	}

	void apply(osg::Geode& geode) // NOLINT
	{
		// Only deal with 1 geometry for now ...
		if (geode.getNumDrawables() > 0)
		{
			osg::Geometry* curGeom = geode.getDrawable(0)->asGeometry();
			if (curGeom)
			{
				osg::Vec3Array* vertices = dynamic_cast<osg::Vec3Array*>(curGeom->getVertexArray());

				auto normals = curGeom->getNormalArray();
				geode.addDrawable(buildGeometry(vertices, normals, osg::Vec4(0.0, 0.0, 1.0, 1.0)));

				auto tangents = curGeom->getVertexAttribArray(TANGENT_VERTEX_ATTRIBUTE_ID);

				auto cast = dynamic_cast<osg::Vec4Array*>(tangents);
				ASSERT_NE(nullptr, cast);
				ASSERT_EQ(vertices->size(), cast->size());
				geode.addDrawable(buildGeometry(vertices, tangents, osg::Vec4(1.0, 0.0, 0.0, 1.0)));

				auto bitangents = curGeom->getVertexAttribArray(BITANGENT_VERTEX_ATTRIBUTE_ID);
				cast = dynamic_cast<osg::Vec4Array*>(bitangents);
				ASSERT_NE(nullptr, cast);
				ASSERT_EQ(vertices->size(), cast->size());
				geode.addDrawable(buildGeometry(vertices, bitangents, osg::Vec4(0.0, 1.0, 0.0, 1.0)));

			}
		}
	}

	osg::Geometry* buildGeometry(osg::Vec3Array* geomVertices, osg::Array* directions, osg::Vec4 color)
	{
		// create Geometry object to store all the vertices and lines primitive.
		osg::Geometry* linesGeom = new osg::Geometry();

		osg::Vec3Array* vertices = new osg::Vec3Array(geomVertices->size() * 2);
		osg::Vec3 direction;
		for (size_t i = 0; i < geomVertices->size(); ++i)
		{

			(*vertices)[i * 2] = (*geomVertices)[i];
			switch (directions->getType())
			{
				case osg::Array::Vec3ArrayType:
					direction = static_cast<const osg::Vec3Array&>(*directions)[i];
					break;
				case osg::Array::Vec4ArrayType:
					for (int j = 0; j < 3; ++j)
					{
						direction[j] = static_cast<const osg::Vec4Array&>(*directions)[i].ptr()[j];
					}
					break;
				default:
					SURGSIM_FAILURE() << "Unhandled Array type.";
			}
			(*vertices)[i * 2 + 1] = (*geomVertices)[i] + direction * m_normalsScale;
		}

		// pass the created vertex array to the points geometry object.
		linesGeom->setVertexArray(vertices);

		// set the colors as before, plus using the above
		osg::Vec4Array* colors = new osg::Vec4Array;
		colors->push_back(color);
		linesGeom->setColorArray(colors, osg::Array::BIND_OVERALL);

		// 	set the normal in the same way color.
		osg::Vec3Array* normals = new osg::Vec3Array;
		normals->push_back(osg::Vec3(0.0f, -1.0f, 0.0f));
		linesGeom->setNormalArray(normals, osg::Array::BIND_OVERALL);

		linesGeom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINES, 0, vertices->size()));

		osg::StateSet* state = linesGeom->getOrCreateStateSet();
		state->setMode(GL_LIGHTING, osg::StateAttribute::PROTECTED | osg::StateAttribute::OFF);

		return linesGeom;
	}

private:
	float m_normalsScale;
};

TEST_F(OsgRepresentationRenderTests, TangentTest)
{
	auto element = std::make_shared<Framework::BasicSceneElement>("sphere");
	auto graphics = std::make_shared<OsgSceneryRepresentation>("sphere");
	graphics->loadModel("OsgRepresentationRenderTests/sphere0_5.obj");
	//graphics->setDrawAsWireFrame(true);

	viewElement->enableManipulator(true);

	camera->setLocalPose(Math::makeRigidTransform(
							 Vector3d(0.0, 0.0, -4.0),
							 Vector3d(0.0, 0.0, 0.0),
							 Vector3d(0.0, 1.0, 0.0)));

	osg::ref_ptr<osg::Node> node = graphics->getOsgNode();
	// Generate normals
	osgUtil::SmoothingVisitor sv;
	node->accept(sv);

	graphics->setGenerateTangents(true);

	LineGeometryVisitor visitor;
	node->accept(visitor);

	element->addComponent(graphics);
	scene->addSceneElement(element);

	runtime->start();

	boost::this_thread::sleep(boost::posix_time::milliseconds(500));
	runtime->stop();
}

};  // namespace Graphics

};  // namespace SurgSim
<|endoftext|>


<commit_before>
#define TRACE_SYMPLECTIC_PARTITIONED_RUNGE_KUTTA_INTEGRATOR

#include "integrators/symplectic_partitioned_runge_kutta_integrator.hpp"

#include <algorithm>
#include <string>
#include <vector>

#include "geometry/sign.hpp"
#include "glog/logging.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "quantities/dimensionless.hpp"
#include "testing_utilities/numerics.hpp"
#include "testing_utilities/statistics.hpp"

using principia::testing_utilities::AbsoluteError;
using testing::AllOf;
using testing::Gt;
using testing::Lt;
using principia::testing_utilities::BidimensionalDatasetMathematicaInput;
using principia::testing_utilities::Slope;
using principia::testing_utilities::PearsonProductMomentCorrelationCoefficient;

namespace principia {
namespace integrators {

namespace {

using quantities::Dimensionless;

inline void compute_harmonic_oscillator_force(double const t,
                                              std::vector<double> const& q,
                                              std::vector<double>* result) {
  (*result)[0] = -q[0];
}

inline void compute_harmonic_oscillator_velocity(std::vector<double> const& p,
                                                  std::vector<double>* result) {
  (*result)[0] = p[0];
}

}  // namespace

class SPRKTest : public testing::Test {
 public:
  static void SetUpTestCase() {
    google::LogToStderr();
  }

 protected:
  void SetUp() override {}

  SPRKIntegrator             integrator_;
  SPRKIntegrator::Parameters parameters_;
  SPRKIntegrator::Solution   solution_;
};

TEST_F(SPRKTest, HarmonicOscillator) {
  parameters_.q0 = {1.0};
  parameters_.p0 = {0.0};
  parameters_.t0 = 0.0;
#ifdef _DEBUG
  parameters_.tmax = 100.0;
#else
  parameters_.tmax = 1000.0;
#endif
  parameters_.Δt = 1.0E-4;
  parameters_.coefficients = integrator_.Order5Optimal();
  parameters_.sampling_period = 1;
  integrator_.Solve(&compute_harmonic_oscillator_force,
                    &compute_harmonic_oscillator_velocity,
                    parameters_, &solution_);
  double q_error = 0;
  double p_error = 0;
  for (size_t i = 0; i < solution_.time.quantities.size(); ++i) {
    q_error = std::max(q_error,
                       std::abs(solution_.position[0].quantities[i] -
                                std::cos(solution_.time.quantities[i])));
    p_error = std::max(p_error,
                       std::abs(solution_.momentum[0].quantities[i] +
                                std::sin(solution_.time.quantities[i])));
  }
  LOG(INFO) << "q_error = " << q_error;
  LOG(INFO) << "p_error = " << p_error;
  EXPECT_THAT(q_error, Lt(2E-16 * parameters_.tmax));
  EXPECT_THAT(p_error, Lt(2E-16 * parameters_.tmax));
}

TEST_F(SPRKTest, Convergence) {
  parameters_.q0 = {1.0};
  parameters_.p0 = {0.0};
  parameters_.t0 = 0.0;
  parameters_.tmax = 100;
  parameters_.coefficients = integrator_.Order5Optimal();
  parameters_.sampling_period = 0;
  // For 0.2 * 1.1⁻²¹ < |Δt| < 0.2 , the correlation between step size and error
  // is very strong. It the step is small enough to converge and large enough to
  // stay clear of floating point inaccuracy.
  parameters_.Δt = 0.2;
  int const step_sizes = 22;
  double const step_reduction = 1.1;
  std::vector<Dimensionless> log_step_sizes(step_sizes);
  std::vector<Dimensionless> log_q_errors(step_sizes);
  std::vector<Dimensionless> log_p_errors(step_sizes);
  for (int i = 0; i < step_sizes; ++i, parameters_.Δt /= step_reduction) {
    solution_ = SPRKIntegrator::Solution();
    integrator_.Solve(&compute_harmonic_oscillator_force,
                      &compute_harmonic_oscillator_velocity,
                      parameters_, &solution_);
    log_step_sizes[i] = std::log10(parameters_.Δt);
    log_q_errors[i] = std::log10(
        std::abs(solution_.position[0].quantities[0] -
                 std::cos(solution_.time.quantities[0])));
    log_p_errors[i] = std::log10(
        std::abs(solution_.momentum[0].quantities[0] +
                 std::sin(solution_.time.quantities[0])));
  }
  google::LogToStderr();
  Dimensionless const q_convergence_order = Slope(log_step_sizes, log_q_errors);
  Dimensionless const q_correlation =
      PearsonProductMomentCorrelationCoefficient(log_step_sizes, log_q_errors);
  LOG(INFO) << "Convergence order in q : " << q_convergence_order;
  LOG(INFO) << "Correlation            : " << q_correlation;
  LOG(INFO) << "Convergence data for q :\n" <<
      BidimensionalDatasetMathematicaInput(log_step_sizes, log_q_errors);
  EXPECT_THAT(q_convergence_order, AllOf(Gt(4.9), Lt(5.1)));
  EXPECT_THAT(q_correlation, AllOf(Gt(0.999), Lt(1.01)));
  Dimensionless const p_convergence_order = Slope(log_step_sizes, log_p_errors);
  Dimensionless const p_correlation =
      PearsonProductMomentCorrelationCoefficient(log_step_sizes, log_p_errors);
  LOG(INFO) << "Convergence order in p : " << p_convergence_order;
  LOG(INFO) << "Correlation            : " << p_correlation;
  LOG(INFO) << "Convergence data for p :\n" <<
      BidimensionalDatasetMathematicaInput(log_step_sizes, log_q_errors);
  EXPECT_THAT(p_convergence_order, AllOf(Gt(5.9), Lt(6.1)));
  EXPECT_THAT(p_correlation, AllOf(Gt(0.999), Lt(1.01)));
}

}  // namespace integrators
}  // namespace principia
<commit_msg>Why is that here?<commit_after>
#define TRACE_SYMPLECTIC_PARTITIONED_RUNGE_KUTTA_INTEGRATOR

#include "integrators/symplectic_partitioned_runge_kutta_integrator.hpp"

#include <algorithm>
#include <string>
#include <vector>

#include "geometry/sign.hpp"
#include "glog/logging.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "quantities/dimensionless.hpp"
#include "testing_utilities/numerics.hpp"
#include "testing_utilities/statistics.hpp"

using principia::testing_utilities::AbsoluteError;
using testing::AllOf;
using testing::Gt;
using testing::Lt;
using principia::testing_utilities::BidimensionalDatasetMathematicaInput;
using principia::testing_utilities::Slope;
using principia::testing_utilities::PearsonProductMomentCorrelationCoefficient;

namespace principia {
namespace integrators {

namespace {

using quantities::Dimensionless;

inline void compute_harmonic_oscillator_force(double const t,
                                              std::vector<double> const& q,
                                              std::vector<double>* result) {
  (*result)[0] = -q[0];
}

inline void compute_harmonic_oscillator_velocity(std::vector<double> const& p,
                                                  std::vector<double>* result) {
  (*result)[0] = p[0];
}

}  // namespace

class SPRKTest : public testing::Test {
 public:
  static void SetUpTestCase() {
    google::LogToStderr();
  }

 protected:
  void SetUp() override {}

  SPRKIntegrator             integrator_;
  SPRKIntegrator::Parameters parameters_;
  SPRKIntegrator::Solution   solution_;
};

TEST_F(SPRKTest, HarmonicOscillator) {
  parameters_.q0 = {1.0};
  parameters_.p0 = {0.0};
  parameters_.t0 = 0.0;
#ifdef _DEBUG
  parameters_.tmax = 100.0;
#else
  parameters_.tmax = 1000.0;
#endif
  parameters_.Δt = 1.0E-4;
  parameters_.coefficients = integrator_.Order5Optimal();
  parameters_.sampling_period = 1;
  integrator_.Solve(&compute_harmonic_oscillator_force,
                    &compute_harmonic_oscillator_velocity,
                    parameters_, &solution_);
  double q_error = 0;
  double p_error = 0;
  for (size_t i = 0; i < solution_.time.quantities.size(); ++i) {
    q_error = std::max(q_error,
                       std::abs(solution_.position[0].quantities[i] -
                                std::cos(solution_.time.quantities[i])));
    p_error = std::max(p_error,
                       std::abs(solution_.momentum[0].quantities[i] +
                                std::sin(solution_.time.quantities[i])));
  }
  LOG(INFO) << "q_error = " << q_error;
  LOG(INFO) << "p_error = " << p_error;
  EXPECT_THAT(q_error, Lt(2E-16 * parameters_.tmax));
  EXPECT_THAT(p_error, Lt(2E-16 * parameters_.tmax));
}

TEST_F(SPRKTest, Convergence) {
  parameters_.q0 = {1.0};
  parameters_.p0 = {0.0};
  parameters_.t0 = 0.0;
  parameters_.tmax = 100;
  parameters_.coefficients = integrator_.Order5Optimal();
  parameters_.sampling_period = 0;
  // For 0.2 * 1.1⁻²¹ < |Δt| < 0.2 , the correlation between step size and error
  // is very strong. It the step is small enough to converge and large enough to
  // stay clear of floating point inaccuracy.
  parameters_.Δt = 0.2;
  int const step_sizes = 22;
  double const step_reduction = 1.1;
  std::vector<Dimensionless> log_step_sizes(step_sizes);
  std::vector<Dimensionless> log_q_errors(step_sizes);
  std::vector<Dimensionless> log_p_errors(step_sizes);
  for (int i = 0; i < step_sizes; ++i, parameters_.Δt /= step_reduction) {
    solution_ = SPRKIntegrator::Solution();
    integrator_.Solve(&compute_harmonic_oscillator_force,
                      &compute_harmonic_oscillator_velocity,
                      parameters_, &solution_);
    log_step_sizes[i] = std::log10(parameters_.Δt);
    log_q_errors[i] = std::log10(
        std::abs(solution_.position[0].quantities[0] -
                 std::cos(solution_.time.quantities[0])));
    log_p_errors[i] = std::log10(
        std::abs(solution_.momentum[0].quantities[0] +
                 std::sin(solution_.time.quantities[0])));
  }
  Dimensionless const q_convergence_order = Slope(log_step_sizes, log_q_errors);
  Dimensionless const q_correlation =
      PearsonProductMomentCorrelationCoefficient(log_step_sizes, log_q_errors);
  LOG(INFO) << "Convergence order in q : " << q_convergence_order;
  LOG(INFO) << "Correlation            : " << q_correlation;
  LOG(INFO) << "Convergence data for q :\n" <<
      BidimensionalDatasetMathematicaInput(log_step_sizes, log_q_errors);
  EXPECT_THAT(q_convergence_order, AllOf(Gt(4.9), Lt(5.1)));
  EXPECT_THAT(q_correlation, AllOf(Gt(0.999), Lt(1.01)));
  Dimensionless const p_convergence_order = Slope(log_step_sizes, log_p_errors);
  Dimensionless const p_correlation =
      PearsonProductMomentCorrelationCoefficient(log_step_sizes, log_p_errors);
  LOG(INFO) << "Convergence order in p : " << p_convergence_order;
  LOG(INFO) << "Correlation            : " << p_correlation;
  LOG(INFO) << "Convergence data for p :\n" <<
      BidimensionalDatasetMathematicaInput(log_step_sizes, log_q_errors);
  EXPECT_THAT(p_convergence_order, AllOf(Gt(5.9), Lt(6.1)));
  EXPECT_THAT(p_correlation, AllOf(Gt(0.999), Lt(1.01)));
}

}  // namespace integrators
}  // namespace principia
<|endoftext|>


<commit_before>#include <mpi.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "pearson.h"

const int T_MAX = 10000;

Formura_Navigator navi;

float frand() {
  return rand() / float(RAND_MAX);
}

void init() {
  for(int y = navi.lower_y; y < navi.upper_y; ++y) {
    for(int x = navi.lower_x; x < navi.upper_x; ++x) {
      U[y][x] = 1;
      V[y][x] = 0;
    }
  }
  for (int y = 118; y < 138; ++y) {
    for (int x = 118; x < 138; ++x) {
      U[y][x] = 0.5+0.01*frand();
      V[y][x] = 0.25+0.01*frand();
    }
  }
}

int main (int argc, char **argv) {
  system("mkdir -p frames");
  srand(time(NULL));
  MPI_Init(&argc, &argv);
  Formura_Init(&navi, MPI_COMM_WORLD);

  init();

  while(navi.time_step < T_MAX) {
    if(navi.time_step % 20 == 0) {
      printf("t = %d\n", navi.time_step);
      char fn[256];
      sprintf(fn, "frames/%06d.txt", navi.time_step);
      FILE *fp = fopen(fn,"w");
      for(int y = navi.lower_y; y < navi.upper_y; ++y) {
        for(int x = navi.lower_x; x < navi.upper_x; ++x) {
          fprintf(fp, "%d %d %f\n", x, y, U[y][x]);
        }
        fprintf(fp, "\n");
      }
      fclose(fp);
    }
    Formura_Forward(&navi);
  }
  MPI_Finalize();
}
<commit_msg>Fix the x-y confusion in pearson-main.cpp<commit_after>#include <mpi.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "pearson.h"

const int T_MAX = 10000;

Formura_Navigator navi;

float frand() {
  return rand() / float(RAND_MAX);
}

void init() {
  for(int x = navi.lower_x; x < navi.upper_x; ++x) {
    for(int y = navi.lower_y; y < navi.upper_y; ++y) {
      U[x][y] = 1;
      V[x][y] = 0;
    }
  }
  for (int x = 58; x < 78; ++x) {
    for (int y = 58; y < 78; ++y) {
      U[x][y] = 0.5+0.01*frand();
      V[x][y] = 0.25+0.01*frand();
    }
  }
}

int main (int argc, char **argv) {
  system("mkdir -p frames");
  srand(time(NULL));
  MPI_Init(&argc, &argv);
  Formura_Init(&navi, MPI_COMM_WORLD);

  init();

  while(navi.time_step < T_MAX) {
    if(navi.time_step % 20 == 0) {
      printf("t = %d\n", navi.time_step);
      char fn[256];
      sprintf(fn, "frames/%06d.txt", navi.time_step);
      FILE *fp = fopen(fn,"w");
      for(int x = navi.lower_x; x < navi.upper_x; ++x) {
        for(int y = navi.lower_y; y < navi.upper_y; ++y) {
          fprintf(fp, "%d %d %f\n", x, y, U[x][y]);
        }
        fprintf(fp, "\n");
      }
      fclose(fp);
    }
    Formura_Forward(&navi);
  }
  MPI_Finalize();
}
<|endoftext|>


<commit_before>/*
 * File:   RetroboxSystem.cpp
 * Author: matthieu
 *
 * Created on 29 novembre 2014, 03:15
 */

#include "RecalboxSystem.h"
#include <stdlib.h>
#include <sys/statvfs.h>
#include <sstream>
#include "Settings.h"
#include <iostream>
#include <fstream>
#include "Log.h"
#include "HttpReq.h"

#include "AudioManager.h"
#include "VolumeControl.h"

#include <stdio.h>
#include <sys/types.h>
#include <ifaddrs.h>
#include <netinet/in.h>
#include <string.h>
#include <arpa/inet.h>

RecalboxSystem::RecalboxSystem() {
}

RecalboxSystem *RecalboxSystem::instance = NULL;

RecalboxSystem *RecalboxSystem::getInstance() {
    if (RecalboxSystem::instance == NULL) {
        RecalboxSystem::instance = new RecalboxSystem();
    }
    return RecalboxSystem::instance;
}

unsigned long RecalboxSystem::getFreeSpaceGB(std::string mountpoint) {
    struct statvfs fiData;
    const char *fnPath = mountpoint.c_str();
    int free = 0;
    if ((statvfs(fnPath, &fiData)) >= 0) {
        free = (fiData.f_bfree * fiData.f_bsize) / (1024 * 1024 * 1024);
    }
    return free;
}

std::string RecalboxSystem::getFreeSpaceInfo() {
    struct statvfs fiData;
    std::string sharePart = Settings::getInstance()->getString("SharePartition");
    if (sharePart.size() > 0) {
        const char *fnPath = sharePart.c_str();
        if ((statvfs(fnPath, &fiData)) < 0) {
            return "";
        } else {
            unsigned long total = (fiData.f_blocks * (fiData.f_bsize / 1024)) / (1024L * 1024L);
            unsigned long free = (fiData.f_bfree * (fiData.f_bsize / 1024)) / (1024L * 1024L);
            unsigned long used = total - free;
            unsigned long percent = used * 100 / total;

            std::ostringstream oss;
            oss << used << "GB/" << total << "GB (" << percent << "%)";
            return oss.str();
        }
    } else {
        return "ERROR";
    }
}

bool RecalboxSystem::isFreeSpaceLimit() {
    std::string sharePart = Settings::getInstance()->getString("SharePartition");
    if (sharePart.size() > 0) {
        return getFreeSpaceGB(sharePart) < 2;
    } else {
        return "ERROR";
    }

}

std::string RecalboxSystem::getVersion() {
    std::string version = Settings::getInstance()->getString("VersionFile");
    if (version.size() > 0) {
        std::ifstream ifs(version);

        if (ifs.good()) {
            std::string contents;
            std::getline(ifs, contents);
            return contents;
        }
    }
    return "";
}

bool RecalboxSystem::needToShowVersionMessage() {
    std::string versionFile = Settings::getInstance()->getString("LastVersionFile");
    if (versionFile.size() > 0) {
        std::ifstream lvifs(versionFile);
        if (lvifs.good()) {
            std::string lastVersion;
            std::getline(lvifs, lastVersion);
            std::string currentVersion = getVersion();
            if (lastVersion == currentVersion) {
                return false;
            }
        }
    }
    return true;
}

bool RecalboxSystem::versionMessageDisplayed() {
    std::string versionFile = Settings::getInstance()->getString("LastVersionFile");
    std::string currentVersion = getVersion();
    std::ostringstream oss;
    oss << "echo " << currentVersion << " > " << versionFile;
    if (system(oss.str().c_str())) {
        LOG(LogWarning) << "Error executing " << oss.str().c_str();
        return false;
    } else {
        LOG(LogInfo) << "Version message displayed ok";
        return true;
    }

    return false;
}

std::string RecalboxSystem::getVersionMessage() {
    std::string versionMessageFile = Settings::getInstance()->getString("VersionMessage");
    if (versionMessageFile.size() > 0) {
        std::ifstream ifs(versionMessageFile);

        if (ifs.good()) {
            std::string contents((std::istreambuf_iterator<char>(ifs)),
                                 std::istreambuf_iterator<char>());
            return contents;
        }
    }
    return "";

}

bool RecalboxSystem::setAudioOutputDevice(std::string device) {
    int commandValue = -1;
    int returnValue = false;

    if (device == "auto") {
        commandValue = 0;
    } else if (device == "jack") {
        commandValue = 1;
    } else if (device == "hdmi") {
        commandValue = 2;
    } else {
        LOG(LogWarning) << "Unable to find audio output device to use !";
    }

    if (commandValue != -1) {
        std::ostringstream oss;
        oss << "amixer cset numid=3 " << commandValue;
        std::string command = oss.str();
        LOG(LogInfo) << "Launching " << command;
        if (system(command.c_str())) {
            LOG(LogWarning) << "Error executing " << command;
            returnValue = false;
        } else {
            LOG(LogInfo) << "Audio output device set to : " << device;
            returnValue = true;
        }
    }
    return returnValue;
}

bool RecalboxSystem::setOverscan(bool enable) {

    std::ostringstream oss;
    oss << Settings::getInstance()->getString("RecalboxSettingScript") << " " << "overscan";
    if (enable) {
        oss << " " << "enable";
    } else {
        oss << " " << "disable";
    }
    std::string command = oss.str();
    LOG(LogInfo) << "Launching " << command;
    if (system(command.c_str())) {
        LOG(LogWarning) << "Error executing " << command;
        return false;
    } else {
        LOG(LogInfo) << "Overscan set to : " << enable;
        return true;
    }

}

bool RecalboxSystem::setOverclock(std::string mode) {
    if (mode != "") {
        std::ostringstream oss;
        oss << Settings::getInstance()->getString("RecalboxSettingScript") << " "
        << "overclock" << " " << mode;
        std::string command = oss.str();
        LOG(LogInfo) << "Launching " << command;
        if (system(command.c_str())) {
            LOG(LogWarning) << "Error executing " << command;
            return false;
        } else {
            LOG(LogInfo) << "Overclocking set to " << mode;
            return true;
        }
    }

    return false;
}

bool RecalboxSystem::updateSystem() {
    std::string updatecommand = Settings::getInstance()->getString("UpdateCommand");
    if (updatecommand.size() > 0) {
        int exitcode = system(updatecommand.c_str());
        return exitcode == 0;
    }
    return false;
}

bool RecalboxSystem::ping() {
    std::string updateserver = Settings::getInstance()->getString("UpdateServer");
    std::string s("ping -c 1 " + updateserver);
    int exitcode = system(s.c_str());
    return exitcode == 0;
}

bool RecalboxSystem::canUpdate() {
    std::ostringstream oss;
    oss << Settings::getInstance()->getString("RecalboxSettingScript") << " " << "canupdate";
    std::string command = oss.str();
    LOG(LogInfo) << "Launching " << command;
    if (system(command.c_str()) == 0) {
        LOG(LogInfo) << "Can update ";
        return true;
    } else {
        LOG(LogInfo) << "Cannot update ";
        return false;
    }
}

bool RecalboxSystem::launchKodi(Window *window) {

    LOG(LogInfo) << "Attempting to launch kodi...";

    AudioManager::getInstance()->deinit();
    VolumeControl::getInstance()->deinit();

    window->deinit();

    std::string command = "/recalbox/scripts/kodilauncher.sh";
    int exitCode = system(command.c_str());

    window->init();
    VolumeControl::getInstance()->init();
    AudioManager::getInstance()->resumeMusic();
    window->normalizeNextUpdate();

    return exitCode == 0;

}

bool RecalboxSystem::enableWifi(std::string ssid, std::string key) {
    std::ostringstream oss;
    oss << Settings::getInstance()->getString("RecalboxSettingScript") << " "
    << "wifi" << " "
    << "enable" << " \""
    << ssid << "\" \"" << key << "\"";
    std::string command = oss.str();
    LOG(LogInfo) << "Launching " << command;
    if (system(command.c_str()) == 0) {
        LOG(LogInfo) << "Wifi enabled ";
        return true;
    } else {
        LOG(LogInfo) << "Cannot enable wifi ";
        return false;
    }
}

bool RecalboxSystem::disableWifi() {
    std::ostringstream oss;
    oss << Settings::getInstance()->getString("RecalboxSettingScript") << " "
    << "wifi" << " "
    << "disable";
    std::string command = oss.str();
    LOG(LogInfo) << "Launching " << command;
    if (system(command.c_str()) == 0) {
        LOG(LogInfo) << "Wifi disabled ";
        return true;
    } else {
        LOG(LogInfo) << "Cannot disable wifi ";
        return false;
    }
}

std::string RecalboxSystem::getRecalboxConfig(std::string key) {
    std::ostringstream oss;
    oss << Settings::getInstance()->getString("RecalboxConfigScript") << " "
    << " -command load "
    << " -key " << key;
    std::string command = oss.str();
    LOG(LogInfo) << "Launching " << command;

    FILE *pipe = popen(command.c_str(), "r");
    if (!pipe) return "ERROR";
    char buffer[128];
    std::string result = "";
    while (!feof(pipe)) {
        if (fgets(buffer, 128, pipe) != NULL)
            result += buffer;
    }
    pclose(pipe);

    return result;
}

bool RecalboxSystem::setRecalboxConfig(std::string key, std::string value) {
    std::ostringstream oss;
    oss << Settings::getInstance()->getString("RecalboxConfigScript") << " "
    << " -command save"
    << " -key " << key
    << " -value " << value;
    std::string command = oss.str();
    LOG(LogInfo) << "Launching " << command;
    if (system(command.c_str()) == 0) {
        LOG(LogInfo) << key << " saved in recalbox.conf";
        return true;
    } else {
        LOG(LogInfo) << "Cannot save " << key << " in recalbox.conf";
        return false;
    }
}

bool RecalboxSystem::reboot() {
    bool success = system("touch /tmp/reboot.please") == 0;
    SDL_Event *quit = new SDL_Event();
    quit->type = SDL_QUIT;
    SDL_PushEvent(quit);
    return success;
}

bool RecalboxSystem::shutdown() {
    bool success = system("touch /tmp/shutdown.please") == 0;
    SDL_Event *quit = new SDL_Event();
    quit->type = SDL_QUIT;
    SDL_PushEvent(quit);
    return success;
}

std::string RecalboxSystem::getIpAdress() {
    struct ifaddrs *ifAddrStruct = NULL;
    struct ifaddrs *ifa = NULL;
    void *tmpAddrPtr = NULL;

    std::string result = "NOT CONNECTED";
    getifaddrs(&ifAddrStruct);

    for (ifa = ifAddrStruct; ifa != NULL; ifa = ifa->ifa_next) {
        if (!ifa->ifa_addr) {
            continue;
        }
        if (ifa->ifa_addr->sa_family == AF_INET) { // check it is IP4
            // is a valid IP4 Address
            tmpAddrPtr = &((struct sockaddr_in *) ifa->ifa_addr)->sin_addr;
            char addressBuffer[INET_ADDRSTRLEN];
            inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN);
            printf("%s IP Address %s\n", ifa->ifa_name, addressBuffer);
            if (std::string(ifa->ifa_name).find("eth") != std::string::npos || std::string(ifa->ifa_name).find("wlan") != std::string::npos) {
                result = std::string(addressBuffer);
            }
        }
    }
    // Seeking for ipv6 if no IPV4
    if (result.compare("NOT CONNECTED") == 0) {
        for (ifa = ifAddrStruct; ifa != NULL; ifa = ifa->ifa_next) {
            if (!ifa->ifa_addr) {
                continue;
            }
            if (ifa->ifa_addr->sa_family == AF_INET6) { // check it is IP6
                // is a valid IP6 Address
                tmpAddrPtr = &((struct sockaddr_in6 *) ifa->ifa_addr)->sin6_addr;
                char addressBuffer[INET6_ADDRSTRLEN];
                inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN);
                printf("%s IP Address %s\n", ifa->ifa_name, addressBuffer);
                if (std::string(ifa->ifa_name).find("eth") != std::string::npos || std::string(ifa->ifa_name).find("wlan") != std::string::npos) {
                    return std::string(addressBuffer);
                }
            }
        }
    }
    if (ifAddrStruct != NULL) freeifaddrs(ifAddrStruct);
    return result;
}

std::vector<std::string> *RecalboxSystem::scanBluetooth() {
    std::vector<std::string> *res = new std::vector<std::string>();
    std::ostringstream oss;
    oss << Settings::getInstance()->getString("RecalboxSettingScript") << " " << "hcitoolscan";
    FILE *pipe = popen(oss.str().c_str(), "r");
    char line[1024];

    if (pipe == NULL) {
        return NULL;
    }

    while (fgets(line, 1024, pipe)) {
        strtok(line, "\n");
        res->push_back(std::string(line));
    }
    pclose(pipe);

    return res;
}

bool RecalboxSystem::pairBluetooth(std::string &controller) {
    std::ostringstream oss;
    oss << Settings::getInstance()->getString("RecalboxSettingScript") << " " << "hiddpair" << " " << controller;
    int exitcode = system(oss.str().c_str());
    return exitcode == 0;
}
<commit_msg>added new kodi launch<commit_after>/*
 * File:   RetroboxSystem.cpp
 * Author: matthieu
 *
 * Created on 29 novembre 2014, 03:15
 */

#include "RecalboxSystem.h"
#include <stdlib.h>
#include <sys/statvfs.h>
#include <sstream>
#include "Settings.h"
#include <iostream>
#include <fstream>
#include "Log.h"
#include "HttpReq.h"

#include "AudioManager.h"
#include "VolumeControl.h"
#include "InputManager.h"

#include <stdio.h>
#include <sys/types.h>
#include <ifaddrs.h>
#include <netinet/in.h>
#include <string.h>
#include <arpa/inet.h>

RecalboxSystem::RecalboxSystem() {
}

RecalboxSystem *RecalboxSystem::instance = NULL;

RecalboxSystem *RecalboxSystem::getInstance() {
    if (RecalboxSystem::instance == NULL) {
        RecalboxSystem::instance = new RecalboxSystem();
    }
    return RecalboxSystem::instance;
}

unsigned long RecalboxSystem::getFreeSpaceGB(std::string mountpoint) {
    struct statvfs fiData;
    const char *fnPath = mountpoint.c_str();
    int free = 0;
    if ((statvfs(fnPath, &fiData)) >= 0) {
        free = (fiData.f_bfree * fiData.f_bsize) / (1024 * 1024 * 1024);
    }
    return free;
}

std::string RecalboxSystem::getFreeSpaceInfo() {
    struct statvfs fiData;
    std::string sharePart = Settings::getInstance()->getString("SharePartition");
    if (sharePart.size() > 0) {
        const char *fnPath = sharePart.c_str();
        if ((statvfs(fnPath, &fiData)) < 0) {
            return "";
        } else {
            unsigned long total = (fiData.f_blocks * (fiData.f_bsize / 1024)) / (1024L * 1024L);
            unsigned long free = (fiData.f_bfree * (fiData.f_bsize / 1024)) / (1024L * 1024L);
            unsigned long used = total - free;
            unsigned long percent = used * 100 / total;

            std::ostringstream oss;
            oss << used << "GB/" << total << "GB (" << percent << "%)";
            return oss.str();
        }
    } else {
        return "ERROR";
    }
}

bool RecalboxSystem::isFreeSpaceLimit() {
    std::string sharePart = Settings::getInstance()->getString("SharePartition");
    if (sharePart.size() > 0) {
        return getFreeSpaceGB(sharePart) < 2;
    } else {
        return "ERROR";
    }

}

std::string RecalboxSystem::getVersion() {
    std::string version = Settings::getInstance()->getString("VersionFile");
    if (version.size() > 0) {
        std::ifstream ifs(version);

        if (ifs.good()) {
            std::string contents;
            std::getline(ifs, contents);
            return contents;
        }
    }
    return "";
}

bool RecalboxSystem::needToShowVersionMessage() {
    std::string versionFile = Settings::getInstance()->getString("LastVersionFile");
    if (versionFile.size() > 0) {
        std::ifstream lvifs(versionFile);
        if (lvifs.good()) {
            std::string lastVersion;
            std::getline(lvifs, lastVersion);
            std::string currentVersion = getVersion();
            if (lastVersion == currentVersion) {
                return false;
            }
        }
    }
    return true;
}

bool RecalboxSystem::versionMessageDisplayed() {
    std::string versionFile = Settings::getInstance()->getString("LastVersionFile");
    std::string currentVersion = getVersion();
    std::ostringstream oss;
    oss << "echo " << currentVersion << " > " << versionFile;
    if (system(oss.str().c_str())) {
        LOG(LogWarning) << "Error executing " << oss.str().c_str();
        return false;
    } else {
        LOG(LogInfo) << "Version message displayed ok";
        return true;
    }

    return false;
}

std::string RecalboxSystem::getVersionMessage() {
    std::string versionMessageFile = Settings::getInstance()->getString("VersionMessage");
    if (versionMessageFile.size() > 0) {
        std::ifstream ifs(versionMessageFile);

        if (ifs.good()) {
            std::string contents((std::istreambuf_iterator<char>(ifs)),
                                 std::istreambuf_iterator<char>());
            return contents;
        }
    }
    return "";

}

bool RecalboxSystem::setAudioOutputDevice(std::string device) {
    int commandValue = -1;
    int returnValue = false;

    if (device == "auto") {
        commandValue = 0;
    } else if (device == "jack") {
        commandValue = 1;
    } else if (device == "hdmi") {
        commandValue = 2;
    } else {
        LOG(LogWarning) << "Unable to find audio output device to use !";
    }

    if (commandValue != -1) {
        std::ostringstream oss;
        oss << "amixer cset numid=3 " << commandValue;
        std::string command = oss.str();
        LOG(LogInfo) << "Launching " << command;
        if (system(command.c_str())) {
            LOG(LogWarning) << "Error executing " << command;
            returnValue = false;
        } else {
            LOG(LogInfo) << "Audio output device set to : " << device;
            returnValue = true;
        }
    }
    return returnValue;
}

bool RecalboxSystem::setOverscan(bool enable) {

    std::ostringstream oss;
    oss << Settings::getInstance()->getString("RecalboxSettingScript") << " " << "overscan";
    if (enable) {
        oss << " " << "enable";
    } else {
        oss << " " << "disable";
    }
    std::string command = oss.str();
    LOG(LogInfo) << "Launching " << command;
    if (system(command.c_str())) {
        LOG(LogWarning) << "Error executing " << command;
        return false;
    } else {
        LOG(LogInfo) << "Overscan set to : " << enable;
        return true;
    }

}

bool RecalboxSystem::setOverclock(std::string mode) {
    if (mode != "") {
        std::ostringstream oss;
        oss << Settings::getInstance()->getString("RecalboxSettingScript") << " "
        << "overclock" << " " << mode;
        std::string command = oss.str();
        LOG(LogInfo) << "Launching " << command;
        if (system(command.c_str())) {
            LOG(LogWarning) << "Error executing " << command;
            return false;
        } else {
            LOG(LogInfo) << "Overclocking set to " << mode;
            return true;
        }
    }

    return false;
}

bool RecalboxSystem::updateSystem() {
    std::string updatecommand = Settings::getInstance()->getString("UpdateCommand");
    if (updatecommand.size() > 0) {
        int exitcode = system(updatecommand.c_str());
        return exitcode == 0;
    }
    return false;
}

bool RecalboxSystem::ping() {
    std::string updateserver = Settings::getInstance()->getString("UpdateServer");
    std::string s("ping -c 1 " + updateserver);
    int exitcode = system(s.c_str());
    return exitcode == 0;
}

bool RecalboxSystem::canUpdate() {
    std::ostringstream oss;
    oss << Settings::getInstance()->getString("RecalboxSettingScript") << " " << "canupdate";
    std::string command = oss.str();
    LOG(LogInfo) << "Launching " << command;
    if (system(command.c_str()) == 0) {
        LOG(LogInfo) << "Can update ";
        return true;
    } else {
        LOG(LogInfo) << "Cannot update ";
        return false;
    }
}

bool RecalboxSystem::launchKodi(Window *window) {

    LOG(LogInfo) << "Attempting to launch kodi...";

    AudioManager::getInstance()->deinit();
    VolumeControl::getInstance()->deinit();

    window->deinit();

    std::string commandline = InputManager::getInstance()->configureEmulators();
    std::string command = "configgen -system kodi -rom '' "+commandline;
    int exitCode = system(command.c_str());

    window->init();
    VolumeControl::getInstance()->init();
    AudioManager::getInstance()->resumeMusic();
    window->normalizeNextUpdate();

    return exitCode == 0;

}

bool RecalboxSystem::enableWifi(std::string ssid, std::string key) {
    std::ostringstream oss;
    oss << Settings::getInstance()->getString("RecalboxSettingScript") << " "
    << "wifi" << " "
    << "enable" << " \""
    << ssid << "\" \"" << key << "\"";
    std::string command = oss.str();
    LOG(LogInfo) << "Launching " << command;
    if (system(command.c_str()) == 0) {
        LOG(LogInfo) << "Wifi enabled ";
        return true;
    } else {
        LOG(LogInfo) << "Cannot enable wifi ";
        return false;
    }
}

bool RecalboxSystem::disableWifi() {
    std::ostringstream oss;
    oss << Settings::getInstance()->getString("RecalboxSettingScript") << " "
    << "wifi" << " "
    << "disable";
    std::string command = oss.str();
    LOG(LogInfo) << "Launching " << command;
    if (system(command.c_str()) == 0) {
        LOG(LogInfo) << "Wifi disabled ";
        return true;
    } else {
        LOG(LogInfo) << "Cannot disable wifi ";
        return false;
    }
}

std::string RecalboxSystem::getRecalboxConfig(std::string key) {
    std::ostringstream oss;
    oss << Settings::getInstance()->getString("RecalboxConfigScript") << " "
    << " -command load "
    << " -key " << key;
    std::string command = oss.str();
    LOG(LogInfo) << "Launching " << command;

    FILE *pipe = popen(command.c_str(), "r");
    if (!pipe) return "ERROR";
    char buffer[128];
    std::string result = "";
    while (!feof(pipe)) {
        if (fgets(buffer, 128, pipe) != NULL)
            result += buffer;
    }
    pclose(pipe);

    return result;
}

bool RecalboxSystem::setRecalboxConfig(std::string key, std::string value) {
    std::ostringstream oss;
    oss << Settings::getInstance()->getString("RecalboxConfigScript") << " "
    << " -command save"
    << " -key " << key
    << " -value " << value;
    std::string command = oss.str();
    LOG(LogInfo) << "Launching " << command;
    if (system(command.c_str()) == 0) {
        LOG(LogInfo) << key << " saved in recalbox.conf";
        return true;
    } else {
        LOG(LogInfo) << "Cannot save " << key << " in recalbox.conf";
        return false;
    }
}

bool RecalboxSystem::reboot() {
    bool success = system("touch /tmp/reboot.please") == 0;
    SDL_Event *quit = new SDL_Event();
    quit->type = SDL_QUIT;
    SDL_PushEvent(quit);
    return success;
}

bool RecalboxSystem::shutdown() {
    bool success = system("touch /tmp/shutdown.please") == 0;
    SDL_Event *quit = new SDL_Event();
    quit->type = SDL_QUIT;
    SDL_PushEvent(quit);
    return success;
}

std::string RecalboxSystem::getIpAdress() {
    struct ifaddrs *ifAddrStruct = NULL;
    struct ifaddrs *ifa = NULL;
    void *tmpAddrPtr = NULL;

    std::string result = "NOT CONNECTED";
    getifaddrs(&ifAddrStruct);

    for (ifa = ifAddrStruct; ifa != NULL; ifa = ifa->ifa_next) {
        if (!ifa->ifa_addr) {
            continue;
        }
        if (ifa->ifa_addr->sa_family == AF_INET) { // check it is IP4
            // is a valid IP4 Address
            tmpAddrPtr = &((struct sockaddr_in *) ifa->ifa_addr)->sin_addr;
            char addressBuffer[INET_ADDRSTRLEN];
            inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN);
            printf("%s IP Address %s\n", ifa->ifa_name, addressBuffer);
            if (std::string(ifa->ifa_name).find("eth") != std::string::npos || std::string(ifa->ifa_name).find("wlan") != std::string::npos) {
                result = std::string(addressBuffer);
            }
        }
    }
    // Seeking for ipv6 if no IPV4
    if (result.compare("NOT CONNECTED") == 0) {
        for (ifa = ifAddrStruct; ifa != NULL; ifa = ifa->ifa_next) {
            if (!ifa->ifa_addr) {
                continue;
            }
            if (ifa->ifa_addr->sa_family == AF_INET6) { // check it is IP6
                // is a valid IP6 Address
                tmpAddrPtr = &((struct sockaddr_in6 *) ifa->ifa_addr)->sin6_addr;
                char addressBuffer[INET6_ADDRSTRLEN];
                inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN);
                printf("%s IP Address %s\n", ifa->ifa_name, addressBuffer);
                if (std::string(ifa->ifa_name).find("eth") != std::string::npos || std::string(ifa->ifa_name).find("wlan") != std::string::npos) {
                    return std::string(addressBuffer);
                }
            }
        }
    }
    if (ifAddrStruct != NULL) freeifaddrs(ifAddrStruct);
    return result;
}

std::vector<std::string> *RecalboxSystem::scanBluetooth() {
    std::vector<std::string> *res = new std::vector<std::string>();
    std::ostringstream oss;
    oss << Settings::getInstance()->getString("RecalboxSettingScript") << " " << "hcitoolscan";
    FILE *pipe = popen(oss.str().c_str(), "r");
    char line[1024];

    if (pipe == NULL) {
        return NULL;
    }

    while (fgets(line, 1024, pipe)) {
        strtok(line, "\n");
        res->push_back(std::string(line));
    }
    pclose(pipe);

    return res;
}

bool RecalboxSystem::pairBluetooth(std::string &controller) {
    std::ostringstream oss;
    oss << Settings::getInstance()->getString("RecalboxSettingScript") << " " << "hiddpair" << " " << controller;
    int exitcode = system(oss.str().c_str());
    return exitcode == 0;
}
<|endoftext|>


<commit_before>/*-----------------------------------------------------------------------------
 This source file is a part of Hopsan

 Copyright (c) 2009 to present year, Hopsan Group

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

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

 For license details and information about the Hopsan Group see the files
 GPLv3 and HOPSANGROUP in the Hopsan source code root directory

 For author and contributor information see the AUTHORS file
-----------------------------------------------------------------------------*/

// $Id$

#include <QString>
#include <QtTest>
#include <QDir>
#include <QtXml>
#include <QFileInfo>
#include "HopsanEssentials.h"
#include "HopsanCoreMacros.h"

#define DEFAULTLIBPATH "../componentLibraries/defaultLibrary"

#ifndef BUILTINDEFAULTCOMPONENTLIB
    #ifdef _WIN32
        #define DEFAULTCOMPONENTLIB DEFAULTLIBPATH "/defaultComponentLibrary" TO_STR(DEBUG_EXT) ".dll"
    #else
        #define DEFAULTCOMPONENTLIB DEFAULTLIBPATH "/libdefaultComponentLibrary" TO_STR(DEBUG_EXT) ".so"
    #endif
#endif

using namespace hopsan;

class DefaultLibraryXMLTest : public QObject
{
    Q_OBJECT

public:
    DefaultLibraryXMLTest();

private Q_SLOTS:
    void initTestCase();
    void testIconPaths();
    void testPortNames();
    void testSourceCodeLink();

private:
    void recurseCollectXMLFiles(const QDir &rDir);
    QFileInfoList mAllXMLFiles;
    HopsanEssentials mHopsanCore;
};

typedef QMap<QString, QString> IconNameMapT;

QDomElement loadXMLFileToDOM(const QFileInfo &rXMLFileInfo, QDomDocument &rDoc)
{
    QFile file(rXMLFileInfo.absoluteFilePath());
    if (!file.open(QIODevice::ReadOnly))
    {
        return QDomElement();
    }

    rDoc.setContent(&file);
    file.close();

    QDomElement root = rDoc.documentElement();
    if (root.tagName() == "hopsanobjectappearance")
    {
        return root;
    }
    else
    {
        return QDomElement();
    }
}

IconNameMapT extractIconPathsFromMO(const QDomElement dom)
{
    IconNameMapT map;
    QDomElement icon = dom.firstChildElement("icons").firstChildElement("icon");
    while (!icon.isNull())
    {
        map.insert(icon.attribute("type"), icon.attribute("path"));
        icon = icon.nextSiblingElement("icon");
    }
    return map;
}

QStringList extractPortNamesFromMO(const QDomElement dom)
{
    QStringList names;
    QDomElement port = dom.firstChildElement("ports").firstChildElement("port");
    while (!port.isNull())
    {
        names.append(port.attribute("name"));
        port = port.nextSiblingElement("port");
    }
    return names;
}

QString extractTypeNameFromMO(const QDomElement dom)
{
    return dom.attribute("typename");
}

QString extractSourceCodeLinkFromMO(const QDomElement dom)
{
    return dom.attribute("sourcecode");
}

DefaultLibraryXMLTest::DefaultLibraryXMLTest()
{
}

void DefaultLibraryXMLTest::initTestCase()
{
    mAllXMLFiles.clear();

    // Loop through all subdirs, to find all XML files, and store them in a list
    QDir libRoot(DEFAULTLIBPATH);
    QVERIFY2(libRoot.exists(), QString("Libroot: %1 could not be found!").arg(DEFAULTLIBPATH).toStdString().c_str());
    recurseCollectXMLFiles(libRoot);

#ifndef BUILTINDEFAULTCOMPONENTLIB
    bool loadOK = mHopsanCore.loadExternalComponentLib(DEFAULTCOMPONENTLIB);
    QVERIFY2(loadOK, "could not load the component library");
#endif
}

void DefaultLibraryXMLTest::testIconPaths()
{
    bool isOK = true;
    for (int i=0; i<mAllXMLFiles.size(); ++i)
    {
        QDomDocument doc;
        QDomElement mo = loadXMLFileToDOM(mAllXMLFiles[i].absoluteFilePath(),doc).firstChildElement("modelobject");
        while(!mo.isNull())
        {
            IconNameMapT map = extractIconPathsFromMO(mo);
            IconNameMapT::iterator it;
            for (it=map.begin(); it!=map.end(); ++it)
            {
                QString relPath = it.value();
                QFileInfo iconFile(mAllXMLFiles[i].absolutePath()+"/"+relPath);
                if (!iconFile.exists())
                {
                    QWARN(QString("The icon file %1 could not be found, linked from xml file: %2").arg(iconFile.absoluteFilePath()).arg(mAllXMLFiles[i].absoluteFilePath()).toStdString().c_str());
                    isOK = false;
                }
            }
            mo = mo.nextSiblingElement("modelobject");
        }
    }
    QVERIFY2(isOK, "There were at least one icon not found!");
}

void DefaultLibraryXMLTest::testPortNames()
{
    bool isOK = true;
    for (int i=0; i<mAllXMLFiles.size(); ++i)
    {
        QDomDocument doc;
        QDomElement mo = loadXMLFileToDOM(mAllXMLFiles[i].absoluteFilePath(),doc).firstChildElement("modelobject");
        while(!mo.isNull())
        {
            QString typeName = extractTypeNameFromMO(mo);
            QStringList portNames = extractPortNamesFromMO(mo);

            Component* pComponent = mHopsanCore.createComponent(typeName.toStdString().c_str());
            if (pComponent)
            {
                for (int p=0; p<portNames.size(); ++p )
                {
                    Port *pPort = pComponent->getPort(portNames[p].toStdString().c_str());
                    if (pPort == 0)
                    {
                        isOK = false;
                        QWARN(QString("Component: %1    Port: %2    exist in XML but not in CODE!").arg(typeName).arg(portNames[p]).toStdString().c_str());
                    }
                }
                mHopsanCore.removeComponent(pComponent);
            }
            else
            {
                QWARN(QString("Component: %1 exist in XML but not in core!").arg(typeName).toStdString().c_str());
            }
            mo = mo.nextSiblingElement("modelobject");
        }
    }
    QVERIFY2(isOK, "There were at least one port name mismatch in your XML and CODE");
}

void DefaultLibraryXMLTest::testSourceCodeLink()
{
    bool isOK = true;
    for (int i=0; i<mAllXMLFiles.size(); ++i)
    {
        QDomDocument doc;
        QDomElement mo = loadXMLFileToDOM(mAllXMLFiles[i].absoluteFilePath(),doc).firstChildElement("modelobject");
        while(!mo.isNull())
        {
            QString typeName = extractTypeNameFromMO(mo);
            QString sourceCode = extractSourceCodeLinkFromMO(mo);

            if (sourceCode.isEmpty())
            {
                QWARN(QString("SourceCode attribute not set for component %1!").arg(typeName).toStdString().c_str());
                //isOK = false;

                // ===================================
                // Add the code automatically
//                QString codefile = typeName+".hpp";
//                mo.setAttribute("sourcecode", codefile);
//                QFile f(mAllXMLFiles[i].absoluteFilePath());
//                if (f.open(QIODevice::WriteOnly | QIODevice::Text))
//                {
//                    QTextStream out(&f);
//                    QDomDocument doc = mo.ownerDocument();
//                    doc.save(out,4);
//                }
//                f.close();
                // ===================================
            }
            else
            {
                QFileInfo sourceFile(mAllXMLFiles[i].absolutePath()+"/"+sourceCode);
                if(!sourceFile.exists())
                {
                    QWARN(QString("SourceCode file: %1 for component %2 is missing!").arg(sourceFile.absoluteFilePath()).arg(typeName).toStdString().c_str());
                    isOK = false;
                }
            }
            mo = mo.nextSiblingElement("modelobject");
        }
    }
    QVERIFY2(isOK, "There were at least one sourcecode link not working!");
}

void DefaultLibraryXMLTest::recurseCollectXMLFiles(const QDir &rDir)
{
    QFileInfoList contents = rDir.entryInfoList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot, QDir::DirsLast);
    for (int i=0;i<contents.size();++i)
    {
        if (contents[i].isFile() && contents[i].suffix().toLower() == "xml")
        {
            mAllXMLFiles.append(contents[i]);
        }
        else if (contents[i].isDir())
        {
            recurseCollectXMLFiles(contents[i].absoluteFilePath());
        }
    }
}

QTEST_APPLESS_MAIN(DefaultLibraryXMLTest)

#include "tst_defaultlibraryxmltest.moc"
<commit_msg>Prevent reporting error for :graphics/ icons (that are built in) actually it should check that they exist, now it just ignores them<commit_after>/*-----------------------------------------------------------------------------
 This source file is a part of Hopsan

 Copyright (c) 2009 to present year, Hopsan Group

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

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

 For license details and information about the Hopsan Group see the files
 GPLv3 and HOPSANGROUP in the Hopsan source code root directory

 For author and contributor information see the AUTHORS file
-----------------------------------------------------------------------------*/

// $Id$

#include <QString>
#include <QtTest>
#include <QDir>
#include <QtXml>
#include <QFileInfo>
#include "HopsanEssentials.h"
#include "HopsanCoreMacros.h"

#define DEFAULTLIBPATH "../componentLibraries/defaultLibrary"

#ifndef BUILTINDEFAULTCOMPONENTLIB
    #ifdef _WIN32
        #define DEFAULTCOMPONENTLIB DEFAULTLIBPATH "/defaultComponentLibrary" TO_STR(DEBUG_EXT) ".dll"
    #else
        #define DEFAULTCOMPONENTLIB DEFAULTLIBPATH "/libdefaultComponentLibrary" TO_STR(DEBUG_EXT) ".so"
    #endif
#endif

using namespace hopsan;

class DefaultLibraryXMLTest : public QObject
{
    Q_OBJECT

public:
    DefaultLibraryXMLTest();

private Q_SLOTS:
    void initTestCase();
    void testIconPaths();
    void testPortNames();
    void testSourceCodeLink();

private:
    void recurseCollectXMLFiles(const QDir &rDir);
    QFileInfoList mAllXMLFiles;
    HopsanEssentials mHopsanCore;
};

typedef QMap<QString, QString> IconNameMapT;

QDomElement loadXMLFileToDOM(const QFileInfo &rXMLFileInfo, QDomDocument &rDoc)
{
    QFile file(rXMLFileInfo.absoluteFilePath());
    if (!file.open(QIODevice::ReadOnly))
    {
        return QDomElement();
    }

    rDoc.setContent(&file);
    file.close();

    QDomElement root = rDoc.documentElement();
    if (root.tagName() == "hopsanobjectappearance")
    {
        return root;
    }
    else
    {
        return QDomElement();
    }
}

IconNameMapT extractIconPathsFromMO(const QDomElement dom)
{
    IconNameMapT map;
    QDomElement icon = dom.firstChildElement("icons").firstChildElement("icon");
    while (!icon.isNull())
    {
        map.insert(icon.attribute("type"), icon.attribute("path"));
        icon = icon.nextSiblingElement("icon");
    }
    return map;
}

QStringList extractPortNamesFromMO(const QDomElement dom)
{
    QStringList names;
    QDomElement port = dom.firstChildElement("ports").firstChildElement("port");
    while (!port.isNull())
    {
        names.append(port.attribute("name"));
        port = port.nextSiblingElement("port");
    }
    return names;
}

QString extractTypeNameFromMO(const QDomElement dom)
{
    return dom.attribute("typename");
}

QString extractSourceCodeLinkFromMO(const QDomElement dom)
{
    return dom.attribute("sourcecode");
}

DefaultLibraryXMLTest::DefaultLibraryXMLTest()
{
}

void DefaultLibraryXMLTest::initTestCase()
{
    mAllXMLFiles.clear();

    // Loop through all subdirs, to find all XML files, and store them in a list
    QDir libRoot(DEFAULTLIBPATH);
    QVERIFY2(libRoot.exists(), QString("Libroot: %1 could not be found!").arg(DEFAULTLIBPATH).toStdString().c_str());
    recurseCollectXMLFiles(libRoot);

#ifndef BUILTINDEFAULTCOMPONENTLIB
    bool loadOK = mHopsanCore.loadExternalComponentLib(DEFAULTCOMPONENTLIB);
    QVERIFY2(loadOK, "could not load the component library");
#endif
}

void DefaultLibraryXMLTest::testIconPaths()
{
    bool isOK = true;
    for (int i=0; i<mAllXMLFiles.size(); ++i)
    {
        QDomDocument doc;
        QDomElement mo = loadXMLFileToDOM(mAllXMLFiles[i].absoluteFilePath(),doc).firstChildElement("modelobject");
        while(!mo.isNull())
        {
            IconNameMapT map = extractIconPathsFromMO(mo);
            IconNameMapT::iterator it;
            for (it=map.begin(); it!=map.end(); ++it)
            {
                QString relPath = it.value();
                QFileInfo iconFile(mAllXMLFiles[i].absolutePath()+"/"+relPath);
                if (!iconFile.exists() && !relPath.startsWith(":graphics/"))
                {
                    QWARN(QString("The icon file %1 could not be found, linked from xml file: %2").arg(iconFile.absoluteFilePath()).arg(mAllXMLFiles[i].absoluteFilePath()).toStdString().c_str());
                    isOK = false;
                }
            }
            mo = mo.nextSiblingElement("modelobject");
        }
    }
    QVERIFY2(isOK, "There were at least one icon not found!");
}

void DefaultLibraryXMLTest::testPortNames()
{
    bool isOK = true;
    for (int i=0; i<mAllXMLFiles.size(); ++i)
    {
        QDomDocument doc;
        QDomElement mo = loadXMLFileToDOM(mAllXMLFiles[i].absoluteFilePath(),doc).firstChildElement("modelobject");
        while(!mo.isNull())
        {
            QString typeName = extractTypeNameFromMO(mo);
            QStringList portNames = extractPortNamesFromMO(mo);

            Component* pComponent = mHopsanCore.createComponent(typeName.toStdString().c_str());
            if (pComponent)
            {
                for (int p=0; p<portNames.size(); ++p )
                {
                    Port *pPort = pComponent->getPort(portNames[p].toStdString().c_str());
                    if (pPort == 0)
                    {
                        isOK = false;
                        QWARN(QString("Component: %1    Port: %2    exist in XML but not in CODE!").arg(typeName).arg(portNames[p]).toStdString().c_str());
                    }
                }
                mHopsanCore.removeComponent(pComponent);
            }
            else
            {
                QWARN(QString("Component: %1 exist in XML but not in core!").arg(typeName).toStdString().c_str());
            }
            mo = mo.nextSiblingElement("modelobject");
        }
    }
    QVERIFY2(isOK, "There were at least one port name mismatch in your XML and CODE");
}

void DefaultLibraryXMLTest::testSourceCodeLink()
{
    bool isOK = true;
    for (int i=0; i<mAllXMLFiles.size(); ++i)
    {
        QDomDocument doc;
        QDomElement mo = loadXMLFileToDOM(mAllXMLFiles[i].absoluteFilePath(),doc).firstChildElement("modelobject");
        while(!mo.isNull())
        {
            QString typeName = extractTypeNameFromMO(mo);
            QString sourceCode = extractSourceCodeLinkFromMO(mo);

            if (sourceCode.isEmpty())
            {
                QWARN(QString("SourceCode attribute not set for component %1!").arg(typeName).toStdString().c_str());
                //isOK = false;

                // ===================================
                // Add the code automatically
//                QString codefile = typeName+".hpp";
//                mo.setAttribute("sourcecode", codefile);
//                QFile f(mAllXMLFiles[i].absoluteFilePath());
//                if (f.open(QIODevice::WriteOnly | QIODevice::Text))
//                {
//                    QTextStream out(&f);
//                    QDomDocument doc = mo.ownerDocument();
//                    doc.save(out,4);
//                }
//                f.close();
                // ===================================
            }
            else
            {
                QFileInfo sourceFile(mAllXMLFiles[i].absolutePath()+"/"+sourceCode);
                if(!sourceFile.exists())
                {
                    QWARN(QString("SourceCode file: %1 for component %2 is missing!").arg(sourceFile.absoluteFilePath()).arg(typeName).toStdString().c_str());
                    isOK = false;
                }
            }
            mo = mo.nextSiblingElement("modelobject");
        }
    }
    QVERIFY2(isOK, "There were at least one sourcecode link not working!");
}

void DefaultLibraryXMLTest::recurseCollectXMLFiles(const QDir &rDir)
{
    QFileInfoList contents = rDir.entryInfoList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot, QDir::DirsLast);
    for (int i=0;i<contents.size();++i)
    {
        if (contents[i].isFile() && contents[i].suffix().toLower() == "xml")
        {
            mAllXMLFiles.append(contents[i]);
        }
        else if (contents[i].isDir())
        {
            recurseCollectXMLFiles(contents[i].absoluteFilePath());
        }
    }
}

QTEST_APPLESS_MAIN(DefaultLibraryXMLTest)

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


<commit_before>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2011 Artem Pavlenko
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *****************************************************************************/

// mapnik
#include <mapnik/projection.hpp>
#include <mapnik/utils.hpp>
#include <mapnik/util/trim.hpp>
#include <mapnik/well_known_srs.hpp>

// stl
#include <stdexcept>

#ifdef MAPNIK_USE_PROJ4
// proj4
#include <proj_api.h>
#if defined(MAPNIK_THREADSAFE) && PJ_VERSION < 480
#include <boost/thread/mutex.hpp>
#ifdef _MSC_VER
#pragma NOTE(mapnik is building against < proj 4.8, reprojection will be faster if you use >= 4.8)
#else
#warning mapnik is building against < proj 4.8, reprojection will be faster if you use >= 4.8
#endif
static boost::mutex mutex_;
#endif

#endif

namespace mapnik {

projection::projection(std::string const& params, bool defer_proj_init)
    : params_(params),
      defer_proj_init_(defer_proj_init),
      proj_(NULL),
      proj_ctx_(NULL)
{
    boost::optional<bool> is_known = is_known_geographic(params_);
    if (is_known){
        is_geographic_ = *is_known;
    }
    else
    {
#ifdef MAPNIK_USE_PROJ4
        init_proj4();
#else
        throw std::runtime_error(std::string("Cannot initialize projection '") + params_ + " ' without proj4 support (-DMAPNIK_USE_PROJ4)");
#endif
    }
    if (!defer_proj_init_) init_proj4();
}

projection::projection(projection const& rhs)
    : params_(rhs.params_),
      defer_proj_init_(rhs.defer_proj_init_),
      is_geographic_(rhs.is_geographic_),
      proj_(NULL),
      proj_ctx_(NULL)
{
    if (!defer_proj_init_) init_proj4();
}

projection& projection::operator=(projection const& rhs)
{
    projection tmp(rhs);
    swap(tmp);
    proj_ctx_ = 0;
    proj_ = 0;
    if (!defer_proj_init_) init_proj4();
    return *this;
}

bool projection::operator==(const projection& other) const
{
    return (params_ == other.params_);
}

bool projection::operator!=(const projection& other) const
{
    return !(*this == other);
}

void projection::init_proj4() const
{
#ifdef MAPNIK_USE_PROJ4
    if (!proj_)
    {
#if PJ_VERSION >= 480
        proj_ctx_ = pj_ctx_alloc();
        proj_ = pj_init_plus_ctx(proj_ctx_, params_.c_str());
        if (!proj_)
        {
            if (proj_ctx_) pj_ctx_free(proj_ctx_);
            throw proj_init_error(params_);
        }
#else
        #if defined(MAPNIK_THREADSAFE)
        mutex::scoped_lock lock(mutex_);
        #endif
        proj_ = pj_init_plus(params_.c_str());
        if (!proj_) throw proj_init_error(params_);
#endif
        is_geographic_ = pj_is_latlong(proj_) ? true : false;
    }
#endif
}

bool projection::is_initialized() const
{
    return proj_ ? true : false;
}

bool projection::is_geographic() const
{
    return is_geographic_;
}

boost::optional<well_known_srs_e> projection::well_known() const
{
    return is_well_known_srs(params_);
}

std::string const& projection::params() const
{
    return params_;
}

void projection::forward(double & x, double &y ) const
{
#ifdef MAPNIK_USE_PROJ4
    if (!proj_)
    {
        throw std::runtime_error("projection::forward not supported unless proj4 is initialized");
    }
    #if defined(MAPNIK_THREADSAFE) && PJ_VERSION < 480
    mutex::scoped_lock lock(mutex_);
    #endif
    projUV p;
    p.u = x * DEG_TO_RAD;
    p.v = y * DEG_TO_RAD;
    p = pj_fwd(p,proj_);
    x = p.u;
    y = p.v;
    if (is_geographic_)
    {
        x *=RAD_TO_DEG;
        y *=RAD_TO_DEG;
    }
#else
    throw std::runtime_error("projection::forward not supported without proj4 support (-DMAPNIK_USE_PROJ4)");
#endif
}

void projection::inverse(double & x,double & y) const
{
#ifdef MAPNIK_USE_PROJ4
    if (!proj_)
    {
        throw std::runtime_error("projection::inverse not supported unless proj4 is initialized");
    }

    #if defined(MAPNIK_THREADSAFE) && PJ_VERSION < 480
    mutex::scoped_lock lock(mutex_);
    #endif
    if (is_geographic_)
    {
        x *=DEG_TO_RAD;
        y *=DEG_TO_RAD;
    }
    projUV p;
    p.u = x;
    p.v = y;
    p = pj_inv(p,proj_);
    x = RAD_TO_DEG * p.u;
    y = RAD_TO_DEG * p.v;
#else
    throw std::runtime_error("projection::inverse not supported without proj4 support (-DMAPNIK_USE_PROJ4)");
#endif
}

projection::~projection()
{
#ifdef MAPNIK_USE_PROJ4
    #if defined(MAPNIK_THREADSAFE) && PJ_VERSION < 480
        mutex::scoped_lock lock(mutex_);
    #endif
        if (proj_) pj_free(proj_);
    #if PJ_VERSION >= 480
        if (proj_ctx_) pj_ctx_free(proj_ctx_);
    #endif
#endif
}

std::string projection::expanded() const
{
#ifdef MAPNIK_USE_PROJ4
    if (proj_) return mapnik::util::trim_copy(pj_get_def( proj_, 0 ));
#endif
    return params_;
}

void projection::swap(projection& rhs)
{
    std::swap(params_,rhs.params_);
    std::swap(defer_proj_init_,rhs.defer_proj_init_);
    std::swap(is_geographic_,rhs.is_geographic_);
}

}
<commit_msg>prevent double-free in mapnik::projection<commit_after>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2011 Artem Pavlenko
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *****************************************************************************/

// mapnik
#include <mapnik/projection.hpp>
#include <mapnik/utils.hpp>
#include <mapnik/util/trim.hpp>
#include <mapnik/well_known_srs.hpp>

// stl
#include <stdexcept>

#ifdef MAPNIK_USE_PROJ4
// proj4
#include <proj_api.h>
#if defined(MAPNIK_THREADSAFE) && PJ_VERSION < 480
#include <boost/thread/mutex.hpp>
#ifdef _MSC_VER
#pragma NOTE(mapnik is building against < proj 4.8, reprojection will be faster if you use >= 4.8)
#else
#warning mapnik is building against < proj 4.8, reprojection will be faster if you use >= 4.8
#endif
static boost::mutex mutex_;
#endif

#endif

namespace mapnik {

projection::projection(std::string const& params, bool defer_proj_init)
    : params_(params),
      defer_proj_init_(defer_proj_init),
      proj_(NULL),
      proj_ctx_(NULL)
{
    boost::optional<bool> is_known = is_known_geographic(params_);
    if (is_known){
        is_geographic_ = *is_known;
    }
    else
    {
#ifdef MAPNIK_USE_PROJ4
        init_proj4();
#else
        throw std::runtime_error(std::string("Cannot initialize projection '") + params_ + " ' without proj4 support (-DMAPNIK_USE_PROJ4)");
#endif
    }
    if (!defer_proj_init_) init_proj4();
}

projection::projection(projection const& rhs)
    : params_(rhs.params_),
      defer_proj_init_(rhs.defer_proj_init_),
      is_geographic_(rhs.is_geographic_),
      proj_(NULL),
      proj_ctx_(NULL)
{
    if (!defer_proj_init_) init_proj4();
}

projection& projection::operator=(projection const& rhs)
{
    projection tmp(rhs);
    swap(tmp);
    proj_ctx_ = 0;
    proj_ = 0;
    if (!defer_proj_init_) init_proj4();
    return *this;
}

bool projection::operator==(const projection& other) const
{
    return (params_ == other.params_);
}

bool projection::operator!=(const projection& other) const
{
    return !(*this == other);
}

void projection::init_proj4() const
{
#ifdef MAPNIK_USE_PROJ4
    if (!proj_)
    {
#if PJ_VERSION >= 480
        proj_ctx_ = pj_ctx_alloc();
        proj_ = pj_init_plus_ctx(proj_ctx_, params_.c_str());
        if (!proj_)
        {
            if (proj_ctx_) {
                pj_ctx_free(proj_ctx_);
                proj_ctx_ = 0;
            }
            throw proj_init_error(params_);
        }
#else
        #if defined(MAPNIK_THREADSAFE)
        mutex::scoped_lock lock(mutex_);
        #endif
        proj_ = pj_init_plus(params_.c_str());
        if (!proj_) throw proj_init_error(params_);
#endif
        is_geographic_ = pj_is_latlong(proj_) ? true : false;
    }
#endif
}

bool projection::is_initialized() const
{
    return proj_ ? true : false;
}

bool projection::is_geographic() const
{
    return is_geographic_;
}

boost::optional<well_known_srs_e> projection::well_known() const
{
    return is_well_known_srs(params_);
}

std::string const& projection::params() const
{
    return params_;
}

void projection::forward(double & x, double &y ) const
{
#ifdef MAPNIK_USE_PROJ4
    if (!proj_)
    {
        throw std::runtime_error("projection::forward not supported unless proj4 is initialized");
    }
    #if defined(MAPNIK_THREADSAFE) && PJ_VERSION < 480
    mutex::scoped_lock lock(mutex_);
    #endif
    projUV p;
    p.u = x * DEG_TO_RAD;
    p.v = y * DEG_TO_RAD;
    p = pj_fwd(p,proj_);
    x = p.u;
    y = p.v;
    if (is_geographic_)
    {
        x *=RAD_TO_DEG;
        y *=RAD_TO_DEG;
    }
#else
    throw std::runtime_error("projection::forward not supported without proj4 support (-DMAPNIK_USE_PROJ4)");
#endif
}

void projection::inverse(double & x,double & y) const
{
#ifdef MAPNIK_USE_PROJ4
    if (!proj_)
    {
        throw std::runtime_error("projection::inverse not supported unless proj4 is initialized");
    }

    #if defined(MAPNIK_THREADSAFE) && PJ_VERSION < 480
    mutex::scoped_lock lock(mutex_);
    #endif
    if (is_geographic_)
    {
        x *=DEG_TO_RAD;
        y *=DEG_TO_RAD;
    }
    projUV p;
    p.u = x;
    p.v = y;
    p = pj_inv(p,proj_);
    x = RAD_TO_DEG * p.u;
    y = RAD_TO_DEG * p.v;
#else
    throw std::runtime_error("projection::inverse not supported without proj4 support (-DMAPNIK_USE_PROJ4)");
#endif
}

projection::~projection()
{
#ifdef MAPNIK_USE_PROJ4
    #if defined(MAPNIK_THREADSAFE) && PJ_VERSION < 480
        mutex::scoped_lock lock(mutex_);
    #endif
        if (proj_) pj_free(proj_);
    #if PJ_VERSION >= 480
        if (proj_ctx_) pj_ctx_free(proj_ctx_);
    #endif
#endif
}

std::string projection::expanded() const
{
#ifdef MAPNIK_USE_PROJ4
    if (proj_) return mapnik::util::trim_copy(pj_get_def( proj_, 0 ));
#endif
    return params_;
}

void projection::swap(projection& rhs)
{
    std::swap(params_,rhs.params_);
    std::swap(defer_proj_init_,rhs.defer_proj_init_);
    std::swap(is_geographic_,rhs.is_geographic_);
}

}
<|endoftext|>


<commit_before>/*{{{
    Copyright (C) 2013 Matthias Kretz <kretz@kde.org>

    Permission to use, copy, modify, and distribute this software
    and its documentation for any purpose and without fee is hereby
    granted, provided that the above copyright notice appear in all
    copies and that both that the copyright notice and this
    permission notice and warranty disclaimer appear in supporting
    documentation, and that the name of the author not be used in
    advertising or publicity pertaining to distribution of the
    software without specific, written prior permission.

    The author disclaim all warranties with regard to this
    software, including all implied warranties of merchantability
    and fitness.  In no event shall the author be liable for any
    special, indirect or consequential damages or any damages
    whatsoever resulting from loss of use, data or profits, whether
    in an action of contract, negligence or other tortious action,
    arising out of or in connection with the use or performance of
    this software.

}}}*/

#include <array>
#include <memory>

#include <Vc/Vc>
#include "../tsc.h"

using Vc::float_v;

/*
 * This example shows how an arbitrary problem scales depending on working-set size and FLOPs per
 * load/store. Understanding this can help to create better implementations.
 */

/*
 * The Runner is a method to generate the different scenarios with all parameters to the Work
 * available as constant expressions.
 * The idea is to have the compiler able to optimize as much as possible so that the actual workload
 * alone is benchmarked.
 *
 * The Runner recursively calls operator() on the Work template class with varying arguments for N
 * and FLOPs.
 */
template<template<std::size_t N, std::size_t M, int, int> class Work, std::size_t N = 256, std::size_t M = 4, int FLOPs = 2> struct Runner
{
    static void run() {
        Work<N, M, (N > 4096 ? 1 : 4096 / N), FLOPs>()();
        Runner<Work, N, M, int(FLOPs * 1.5)>::run();
    }
};

template<template<std::size_t N, std::size_t M, int, int> class Work, std::size_t N, std::size_t M> struct Runner<Work, N, M, 211>
{
    static void run() {
        Runner<Work, N * 2, M>::run();
    }
};
template<template<std::size_t N, std::size_t M, int, int> class Work, std::size_t M, int FLOPs> struct Runner<Work, 256 * 1024 * 1024, M, FLOPs>
{
    static void run() {
    }
};

/*
 * The Flops helper struct generates code that executes FLOPs many floating-point SIMD instructions
 * (add, sub, and mul)
 */
template<int FLOPs> struct Flops
{
    inline float_v operator()(float_v a, float_v b, float_v c)
    {
        typedef Flops<(FLOPs - 5) / 2> F1;
        typedef Flops<(FLOPs - 4) / 2> F2;
        return F1()(a + b, a * b, c) + F2()(a * c, b + c, a);
    }
};

template<> inline float_v Flops<2>::operator()(float_v a, float_v b, float_v c)
{
    return a * b + c;
}
template<> inline float_v Flops<3>::operator()(float_v a, float_v b, float_v c)
{
    return a * b + (c - a);
}
template<> inline float_v Flops<4>::operator()(float_v a, float_v b, float_v c)
{
    return (a * b + c) + a * c;
}
template<> inline float_v Flops<5>::operator()(float_v a, float_v b, float_v c)
{
    return a * b + (a + c) + a * c;
}
template<> inline float_v Flops<6>::operator()(float_v a, float_v b, float_v c)
{
    return (a * b + (a + c)) + (a * c - b);
}
template<> inline float_v Flops<7>::operator()(float_v a, float_v b, float_v c)
{
    return (a * b + (a + c)) + (a * c - (b + c));
}
template<> inline float_v Flops<8>::operator()(float_v a, float_v b, float_v c)
{
    return (a * b + (a + c) + b) + (a * c - (b + c));
}

/*
 * This is the benchmark code. It is called from Runner and uses Flops to do the work.
 */
template<std::size_t _N, std::size_t M, int Repetitions, int FLOPs>
struct ScaleWorkingSetSize
{
    void operator()()
    {
        constexpr std::size_t N = _N / sizeof(float_v) + 3 * 16 / float_v::Size;
        typedef std::array<std::array<float_v, N>, M> Cont;
        auto data = Vc::make_unique<Cont, Vc::AlignOnPage>();
        for (auto &arr : *data) {
            for (auto &value : arr) {
                value = float_v::Random();
            }
        }

        TimeStampCounter tsc;
        double throughput = 0.;
        for (std::size_t i = 0; i < 2 + 512 / N; ++i) {
            tsc.start();
            // ------------- start of the benchmarked code ---------------
            for (int repetitions = 0; repetitions < Repetitions; ++repetitions) {
                for (std::size_t m = 0; m < M; ++m) {
                    for (std::size_t n = 0; n < N; ++n) {
                        (*data)[m][n] = Flops<FLOPs>()((*data)[(m + 1) % M][n],
                                (*data)[(m + 2) % M][n],
                                (*data)[(m + 3) % M][n]);
                    }
                }
            }
            // -------------- end of the benchmarked code ----------------
            tsc.stop();

            throughput = std::max(throughput, (Repetitions * M * N * float_v::Size * FLOPs) / static_cast<double>(tsc.cycles()));
        }

        const long bytes = N * M * sizeof(float_v);
        printf("%10lu Byte | %4.2f FLOP/Byte | %4.1f FLOP/cycle\n", bytes, static_cast<double>(float_v::Size * FLOPs) / (4 * sizeof(float_v)), throughput
                );
    }
};

int Vc_CDECL main()
{
    ScaleWorkingSetSize<256, 4, 10, 2>()();
    printf("%10s | %4s | %4s\n", "Working-Set Size", "FLOPs per Byte", "Throughput (FLOPs/Cycle)");
    Runner<ScaleWorkingSetSize>::run();
    return 0;
}
<commit_msg>Examples: End recursion earlier to limit GCC memory consumption<commit_after>/*{{{
    Copyright (C) 2013 Matthias Kretz <kretz@kde.org>

    Permission to use, copy, modify, and distribute this software
    and its documentation for any purpose and without fee is hereby
    granted, provided that the above copyright notice appear in all
    copies and that both that the copyright notice and this
    permission notice and warranty disclaimer appear in supporting
    documentation, and that the name of the author not be used in
    advertising or publicity pertaining to distribution of the
    software without specific, written prior permission.

    The author disclaim all warranties with regard to this
    software, including all implied warranties of merchantability
    and fitness.  In no event shall the author be liable for any
    special, indirect or consequential damages or any damages
    whatsoever resulting from loss of use, data or profits, whether
    in an action of contract, negligence or other tortious action,
    arising out of or in connection with the use or performance of
    this software.

}}}*/

#include <array>
#include <memory>

#include <Vc/Vc>
#include "../tsc.h"

using Vc::float_v;

/*
 * This example shows how an arbitrary problem scales depending on working-set size and FLOPs per
 * load/store. Understanding this can help to create better implementations.
 */

/*
 * The Runner is a method to generate the different scenarios with all parameters to the Work
 * available as constant expressions.
 * The idea is to have the compiler able to optimize as much as possible so that the actual workload
 * alone is benchmarked.
 *
 * The Runner recursively calls operator() on the Work template class with varying arguments for N
 * and FLOPs.
 */
template<template<std::size_t N, std::size_t M, int, int> class Work, std::size_t N = 256, std::size_t M = 4> struct Runner
{
    static void run() {
        Work<N, M, (N > 4096 ? 1 : 4096 / N), 2>()();
        Work<N, M, (N > 4096 ? 1 : 4096 / N), 3>()();
        Work<N, M, (N > 4096 ? 1 : 4096 / N), 4>()();
        Work<N, M, (N > 4096 ? 1 : 4096 / N), 6>()();
        Work<N, M, (N > 4096 ? 1 : 4096 / N), 9>()();
        Work<N, M, (N > 4096 ? 1 : 4096 / N), 13>()();
        Work<N, M, (N > 4096 ? 1 : 4096 / N), 19>()();
        Work<N, M, (N > 4096 ? 1 : 4096 / N), 28>()();
        Work<N, M, (N > 4096 ? 1 : 4096 / N), 42>()();
        Work<N, M, (N > 4096 ? 1 : 4096 / N), 63>()();
        Work<N, M, (N > 4096 ? 1 : 4096 / N), 94>()();
        Work<N, M, (N > 4096 ? 1 : 4096 / N), 141>()();
        Work<N, M, (N > 4096 ? 1 : 4096 / N), 211>()();
        Runner<Work, N * 2, M>::run();
    }
};

template <template <std::size_t N, std::size_t M, int, int> class Work, std::size_t M>
struct Runner<Work, 256 * 1024 * 32,  // don't make this number larger, otherwise GCC6
                                      // blows up with Vc::Scalar to 10GB of memory usage
              M> {
    static void run() {
    }
};

/*
 * The Flops helper struct generates code that executes FLOPs many floating-point SIMD instructions
 * (add, sub, and mul)
 */
template<int FLOPs> struct Flops
{
    inline float_v operator()(float_v a, float_v b, float_v c)
    {
        typedef Flops<(FLOPs - 5) / 2> F1;
        typedef Flops<(FLOPs - 4) / 2> F2;
        return F1()(a + b, a * b, c) + F2()(a * c, b + c, a);
    }
};

template<> inline float_v Flops<2>::operator()(float_v a, float_v b, float_v c)
{
    return a * b + c;
}
template<> inline float_v Flops<3>::operator()(float_v a, float_v b, float_v c)
{
    return a * b + (c - a);
}
template<> inline float_v Flops<4>::operator()(float_v a, float_v b, float_v c)
{
    return (a * b + c) + a * c;
}
template<> inline float_v Flops<5>::operator()(float_v a, float_v b, float_v c)
{
    return a * b + (a + c) + a * c;
}
template<> inline float_v Flops<6>::operator()(float_v a, float_v b, float_v c)
{
    return (a * b + (a + c)) + (a * c - b);
}
template<> inline float_v Flops<7>::operator()(float_v a, float_v b, float_v c)
{
    return (a * b + (a + c)) + (a * c - (b + c));
}
template<> inline float_v Flops<8>::operator()(float_v a, float_v b, float_v c)
{
    return (a * b + (a + c) + b) + (a * c - (b + c));
}

/*
 * This is the benchmark code. It is called from Runner and uses Flops to do the work.
 */
template<std::size_t _N, std::size_t M, int Repetitions, int FLOPs>
struct ScaleWorkingSetSize
{
    void operator()()
    {
        constexpr std::size_t N = _N / sizeof(float_v) + 3 * 16 / float_v::Size;
        typedef std::array<std::array<float_v, N>, M> Cont;
        auto data = Vc::make_unique<Cont, Vc::AlignOnPage>();
        for (auto &arr : *data) {
            for (auto &value : arr) {
                value = float_v::Random();
            }
        }

        TimeStampCounter tsc;
        double throughput = 0.;
        for (std::size_t i = 0; i < 2 + 512 / N; ++i) {
            tsc.start();
            // ------------- start of the benchmarked code ---------------
            for (int repetitions = 0; repetitions < Repetitions; ++repetitions) {
                for (std::size_t m = 0; m < M; ++m) {
                    for (std::size_t n = 0; n < N; ++n) {
                        (*data)[m][n] = Flops<FLOPs>()((*data)[(m + 1) % M][n],
                                (*data)[(m + 2) % M][n],
                                (*data)[(m + 3) % M][n]);
                    }
                }
            }
            // -------------- end of the benchmarked code ----------------
            tsc.stop();

            throughput = std::max(throughput, (Repetitions * M * N * float_v::Size * FLOPs) / static_cast<double>(tsc.cycles()));
        }

        const long bytes = N * M * sizeof(float_v);
        printf("%10lu Byte | %4.2f FLOP/Byte | %4.1f FLOP/cycle\n", bytes, static_cast<double>(float_v::Size * FLOPs) / (4 * sizeof(float_v)), throughput
                );
    }
};

int Vc_CDECL main()
{
    ScaleWorkingSetSize<256, 4, 10, 2>()();
    printf("%10s | %4s | %4s\n", "Working-Set Size", "FLOPs per Byte", "Throughput (FLOPs/Cycle)");
    Runner<ScaleWorkingSetSize>::run();
    return 0;
}
<|endoftext|>