<commit_before>#include "Method_GNEB.h"

#include "Manifoldmath.h"
#include "Cubic_Hermite_Spline.h"
#include "IO.h"
#include "Timing.h"

#include "Optimizer_Heun.h"
#include "Optimizer_SIB.h"
#include "Vectormath.h"

#include"Logging.h"

#include <iostream>
#include <math.h>

using namespace Utility;

namespace Engine
{
    Method_GNEB::Method_GNEB(std::shared_ptr<Data::Spin_System_Chain> chain, int idx_chain) :
		Method(chain->gneb_parameters, -1, idx_chain), chain(chain)
	{
		this->systems = chain->images;
		this->SenderName = Utility::Log_Sender::GNEB;

		int noi = chain->noi;
		int nos = chain->images[0]->nos;

		this->energies = std::vector<double>(noi, 0.0);
		this->Rx = std::vector<double>(noi, 0.0);

		// We assume that the chain is not converged before the first iteration
		this->force_maxAbsComponent = this->chain->gneb_parameters->force_convergence + 1.0;

		// Tangents
		this->tangents = std::vector<std::vector<double>>(noi, std::vector<double>(3 * nos));	// [noi][3nos]
		// Forces
		this->F_total    = std::vector<std::vector<double>>(noi, std::vector<double>(3 * nos));	// [noi][3nos]
		this->F_gradient = std::vector<std::vector<double>>(noi, std::vector<double>(3 * nos));	// [noi][3nos]
		this->F_spring   = std::vector<std::vector<double>>(noi, std::vector<double>(3 * nos));	// [noi][3nos]
	}

	void Method_GNEB::Calculate_Force(std::vector<std::shared_ptr<std::vector<double>>> configurations, std::vector<std::vector<double>> & forces)
	{
		int nos = configurations[0]->size()/3;

		// We assume here that we receive a vector of configurations that corresponds to the vector of systems we gave the optimizer.
		//		The Optimizer shuld respect this, but there is no way to enforce it.
		// Get Energy and Effective Field of configurations
		for (int i = 0; i < chain->noi; ++i)
		{
			// Calculate the Energy of the image
			energies[i] = this->chain->images[i]->hamiltonian->Energy(*configurations[i]);
			if (i>0) Rx[i] = Rx[i - 1] + Utility::Manifoldmath::Dist_Geodesic(*configurations[i], *configurations[i - 1]);
		}

		// Calculate relevant tangent to magnetisation sphere, considering also the energies of images
		Utility::Manifoldmath::Tangents(configurations, energies, tangents);

		// Get the total force on the image chain
		// Loop over images to calculate the total Effective Field on each Image
		for (int img = 1; img < chain->noi - 1; ++img)
		{
			auto& image = *configurations[img];
			// The gradient force (unprojected) is simply the effective field
			this->chain->images[img]->hamiltonian->Effective_Field(image, F_gradient[img]);

			// Calculate Force
			if (chain->climbing_image[img])
			{
				// We reverse the component in tangent direction
				Utility::Manifoldmath::Project_Reverse(F_gradient[img], tangents[img]);
				// And Spring Force is zero
				F_total[img] = F_gradient[img];
			}
			else if (chain->falling_image[img])
			{
				// We project the gradient force orthogonal to the tangent
				// If anything, project orthogonal to the spins... idiot! But Heun already does that.
				//Utility::Manifoldmath::Project_Orthogonal(F_gradient[img], this->c->tangents[img]);
				// Spring Force is zero
				F_total[img] = F_gradient[img];
			}
			else
			{

				// We project the gradient force orthogonal to the SPIN
				//Utility::Manifoldmath::Project_Orthogonal(F_gradient[img], this->c->tangents[img]);
				// Get the scalar product of the vectors
				double v1v2 = 0.0;
				int dim;
				// Take out component in direction of v2
				for (int i = 0; i < nos; ++i)
				{
					v1v2 = 0.0;
					for (dim = 0; dim < 3; ++dim)
					{
						v1v2 += F_gradient[img][i+dim*nos] * image[i+dim*nos];
					}
					for (dim = 0; dim < 3; ++dim)
					{
						F_gradient[img][i + dim*nos] = F_gradient[img][i + dim*nos] - v1v2 * image[i + dim*nos];
					}
				}

				// We project the gradient force orthogonal to the TANGENT
				//Utility::Manifoldmath::Project_Orthogonal(F_gradient[img], this->c->tangents[img]);
				// Get the scalar product of the vectors
				v1v2 = 0.0;
				for (int i = 0; i < 3*nos; ++i)
				{
					v1v2 += F_gradient[img][i] * tangents[img][i];
				}
				// Take out component in direction of v2
				for (int i = 0; i < 3 * nos; ++i)
				{
					F_gradient[img][i] = F_gradient[img][i] - v1v2 * tangents[img][i];
				}

				// Calculate the spring force
				//spring_forces(:, : ) = spring_constant *(dist_geodesic(NOS, IMAGES_LAST(idx_img + 1, :, : ), IMAGES(idx_img, :, : )) - dist_geodesic(NOS, IMAGES(idx_img, :, : ), IMAGES_LAST(idx_img - 1, :, : )))* tangents(:, : );
				double d = this->chain->gneb_parameters->spring_constant * (Rx[img+1] - Rx[img-1]);
				for (unsigned int i = 0; i < F_spring[0].size(); ++i)
				{
					F_spring[img][i] = d * tangents[img][i];
				}

				// Calculate the total force
				for (int j = 0; j < 3 * nos; ++j)
				{
					F_total[img][j] = F_gradient[img][j] + F_spring[img][j];
				}

				// Copy out
				forces[img] = F_total[img];
			}// end if climbing
		}// end for img=1..noi-1
	}// end Calculate

	bool Method_GNEB::Force_Converged()
	{
		// return this->isConverged;
		if (this->force_maxAbsComponent < this->chain->gneb_parameters->force_convergence) return true;
		return false;
	}

	bool Method_GNEB::Iterations_Allowed()
	{
		return this->chain->iteration_allowed;
	}

	void Method_GNEB::Hook_Pre_Iteration()
	{

	}

	void Method_GNEB::Hook_Post_Iteration()
	{
		// --- Convergence Parameter Update
		this->force_maxAbsComponent = 0;
		for (int img = 1; img < chain->noi - 1; ++img)
		{
			double fmax = this->Force_on_Image_MaxAbsComponent(*(systems[img]->spins), F_total[img]);
			// TODO: how to handle convergence??
			// if (fmax > this->parameters->force_convergence) this->isConverged = false;
			if (fmax > this->force_maxAbsComponent) this->force_maxAbsComponent = fmax;
		}

		// --- Chain Data Update
		// Calculate the inclinations at the data points
		std::vector<double> dE_dRx(chain->noi, 0);
		for (int i = 0; i < chain->noi; ++i)
		{
			// dy/dx
			for (int j = 0; j < 3 * chain->images[i]->nos; ++j)
			{
				dE_dRx[i] += this->F_gradient[i][j] * this->tangents[i][j];
			}
		}
		// Interpolate data points
		auto interp = Utility::Cubic_Hermite_Spline::Interpolate(this->Rx, this->energies, dE_dRx, chain->gneb_parameters->n_E_interpolations);
		// Update the chain
		//		Rx
		chain->Rx = this->Rx;
		//		E
		for (int img = 1; img < chain->noi; ++img) chain->images[img]->E = this->energies[img];
		//		Rx interpolated
		chain->Rx_interpolated = interp[0];
		//		E interpolated
		chain->E_interpolated  = interp[1];
	}

	void Method_GNEB::Finalize()
    {
        this->chain->iteration_allowed=false;
    }

	void Method_GNEB::Save_Current(std::string starttime, int iteration, bool initial, bool final)
	{

		// Get the file suffix
		std::string suffix = "";
		if (final) suffix = "_final";
		else suffix = "";

		// always formatting to 6 digits may be problematic!
		auto s_iter = IO::int_to_formatted_string(iteration, 6);

		// Save current Image Chain
		auto imagesFile = this->chain->gneb_parameters->output_folder + "/" + starttime + "_Images_" + s_iter + suffix + ".txt";
		Utility::IO::Save_SpinChain_Configuration(this->chain, imagesFile);

		// Save current Energies with reaction coordinates
		auto energiesFile = this->chain->gneb_parameters->output_folder + "/" + starttime + "_E_Images_" + s_iter + suffix + ".txt";
		//		Check if Energy File exists and write Header if it doesn't
		std::ifstream f(energiesFile);
		if (!f.good()) Utility::IO::Write_Energy_Header(energiesFile);
		//		Save
		Utility::IO::Save_Energies(*this->chain, iteration, energiesFile);

		// Save interpolated Energies
		auto energiesInterpFile = this->chain->gneb_parameters->output_folder + "/" + starttime + "_E_interp_Images_" + s_iter + suffix + ".txt";
		Utility::IO::Save_Energies_Interpolated(*this->chain, energiesInterpFile);

		// Save Log
		Log.Append_to_File();
	}

	// Optimizer name as string
    std::string Method_GNEB::Name() { return "GNEB"; }
}<commit_msg>BUGFIX: GNEB method - calculation of spring force The spring force was incorrectly calculated, leading to idiotic results. The orthogonalisation of the gradient forces w.r.t. the spins has been removed from GNEB, as it is already automatically done by the Optimizers.<commit_after>#include "Method_GNEB.h"

#include "Manifoldmath.h"
#include "Cubic_Hermite_Spline.h"
#include "IO.h"
#include "Timing.h"

#include "Optimizer_Heun.h"
#include "Optimizer_SIB.h"
#include "Vectormath.h"

#include"Logging.h"

#include <iostream>
#include <math.h>

using namespace Utility;

namespace Engine
{
    Method_GNEB::Method_GNEB(std::shared_ptr<Data::Spin_System_Chain> chain, int idx_chain) :
		Method(chain->gneb_parameters, -1, idx_chain), chain(chain)
	{
		this->systems = chain->images;
		this->SenderName = Utility::Log_Sender::GNEB;

		int noi = chain->noi;
		int nos = chain->images[0]->nos;

		this->energies = std::vector<double>(noi, 0.0);
		this->Rx = std::vector<double>(noi, 0.0);

		// We assume that the chain is not converged before the first iteration
		this->force_maxAbsComponent = this->chain->gneb_parameters->force_convergence + 1.0;

		// Tangents
		this->tangents = std::vector<std::vector<double>>(noi, std::vector<double>(3 * nos));	// [noi][3nos]
		// Forces
		this->F_total    = std::vector<std::vector<double>>(noi, std::vector<double>(3 * nos));	// [noi][3nos]
		this->F_gradient = std::vector<std::vector<double>>(noi, std::vector<double>(3 * nos));	// [noi][3nos]
		this->F_spring   = std::vector<std::vector<double>>(noi, std::vector<double>(3 * nos));	// [noi][3nos]
	}

	void Method_GNEB::Calculate_Force(std::vector<std::shared_ptr<std::vector<double>>> configurations, std::vector<std::vector<double>> & forces)
	{
		int nos = configurations[0]->size()/3;

		// We assume here that we receive a vector of configurations that corresponds to the vector of systems we gave the optimizer.
		//		The Optimizer shuld respect this, but there is no way to enforce it.
		// Get Energy and Effective Field of configurations
		for (int i = 0; i < chain->noi; ++i)
		{
			// Calculate the Energy of the image
			energies[i] = this->chain->images[i]->hamiltonian->Energy(*configurations[i]);
			if (i>0) Rx[i] = Rx[i - 1] + Utility::Manifoldmath::Dist_Geodesic(*configurations[i], *configurations[i - 1]);
		}

		// Calculate relevant tangent to magnetisation sphere, considering also the energies of images
		Utility::Manifoldmath::Tangents(configurations, energies, tangents);

		// Get the total force on the image chain
		// Loop over images to calculate the total Effective Field on each Image
		for (int img = 1; img < chain->noi - 1; ++img)
		{
			auto& image = *configurations[img];
			// The gradient force (unprojected) is simply the effective field
			this->chain->images[img]->hamiltonian->Effective_Field(image, F_gradient[img]);

			// Calculate Force
			if (chain->climbing_image[img])
			{
				// We reverse the component in tangent direction
				Utility::Manifoldmath::Project_Reverse(F_gradient[img], tangents[img]);
				// And Spring Force is zero
				F_total[img] = F_gradient[img];
			}
			else if (chain->falling_image[img])
			{
				// We project the gradient force orthogonal to the tangent
				// If anything, project orthogonal to the spins... idiot! But Heun already does that.
				//Utility::Manifoldmath::Project_Orthogonal(F_gradient[img], this->c->tangents[img]);
				// Spring Force is zero
				F_total[img] = F_gradient[img];
			}
			else
			{
				// We project the gradient force orthogonal to the SPIN
				//Utility::Manifoldmath::Project_Orthogonal(F_gradient[img], this->c->tangents[img]);
				// Get the scalar product of the vectors
				// double v1v2 = 0.0;
				// int dim;
				// // Take out component in direction of v2
				// for (int i = 0; i < nos; ++i)
				// {
				// 	v1v2 = 0.0;
				// 	for (dim = 0; dim < 3; ++dim)
				// 	{
				// 		v1v2 += F_gradient[img][i+dim*nos] * image[i+dim*nos];
				// 	}
				// 	for (dim = 0; dim < 3; ++dim)
				// 	{
				// 		F_gradient[img][i + dim*nos] = F_gradient[img][i + dim*nos] - v1v2 * image[i + dim*nos];
				// 	}
				// }

				// We project the gradient force orthogonal to the TANGENT
				//Utility::Manifoldmath::Project_Orthogonal(F_gradient[img], this->c->tangents[img]);
				// Get the scalar product of the vectors
				double v1v2 = 0.0;
				for (int i = 0; i < 3*nos; ++i)
				{
					v1v2 += F_gradient[img][i] * tangents[img][i];
				}
				// Take out component in direction of v2
				for (int i = 0; i < 3 * nos; ++i)
				{
					F_gradient[img][i] = F_gradient[img][i] - v1v2 * tangents[img][i];
				}

				// Calculate the spring force
				//spring_forces(:, : ) = spring_constant *(dist_geodesic(NOS, IMAGES_LAST(idx_img + 1, :, : ), IMAGES(idx_img, :, : )) - dist_geodesic(NOS, IMAGES(idx_img, :, : ), IMAGES_LAST(idx_img - 1, :, : )))* tangents(:, : );
				double d = this->chain->gneb_parameters->spring_constant * (Rx[img+1] - 2*Rx[img] + Rx[img-1]);
				for (unsigned int i = 0; i < F_spring[0].size(); ++i)
				{
					F_spring[img][i] = d * tangents[img][i];
				}

				// Calculate the total force
				for (int j = 0; j < 3 * nos; ++j)
				{
					F_total[img][j] = F_gradient[img][j] + F_spring[img][j];
				}

				// Copy out
				forces[img] = F_total[img];
			}// end if climbing
		}// end for img=1..noi-1
	}// end Calculate

	bool Method_GNEB::Force_Converged()
	{
		// return this->isConverged;
		if (this->force_maxAbsComponent < this->chain->gneb_parameters->force_convergence) return true;
		return false;
	}

	bool Method_GNEB::Iterations_Allowed()
	{
		return this->chain->iteration_allowed;
	}

	void Method_GNEB::Hook_Pre_Iteration()
	{

	}

	void Method_GNEB::Hook_Post_Iteration()
	{
		// --- Convergence Parameter Update
		this->force_maxAbsComponent = 0;
		for (int img = 1; img < chain->noi - 1; ++img)
		{
			double fmax = this->Force_on_Image_MaxAbsComponent(*(systems[img]->spins), F_total[img]);
			// TODO: how to handle convergence??
			// if (fmax > this->parameters->force_convergence) this->isConverged = false;
			if (fmax > this->force_maxAbsComponent) this->force_maxAbsComponent = fmax;
		}

		// --- Chain Data Update
		// Calculate the inclinations at the data points
		std::vector<double> dE_dRx(chain->noi, 0);
		for (int i = 0; i < chain->noi; ++i)
		{
			// dy/dx
			for (int j = 0; j < 3 * chain->images[i]->nos; ++j)
			{
				dE_dRx[i] += this->F_gradient[i][j] * this->tangents[i][j];
			}
		}
		// Interpolate data points
		auto interp = Utility::Cubic_Hermite_Spline::Interpolate(this->Rx, this->energies, dE_dRx, chain->gneb_parameters->n_E_interpolations);
		// Update the chain
		//		Rx
		chain->Rx = this->Rx;
		//		E
		for (int img = 1; img < chain->noi; ++img) chain->images[img]->E = this->energies[img];
		//		Rx interpolated
		chain->Rx_interpolated = interp[0];
		//		E interpolated
		chain->E_interpolated  = interp[1];
	}

	void Method_GNEB::Finalize()
    {
        this->chain->iteration_allowed=false;
    }

	void Method_GNEB::Save_Current(std::string starttime, int iteration, bool initial, bool final)
	{

		// Get the file suffix
		std::string suffix = "";
		if (final) suffix = "_final";
		else suffix = "";

		// always formatting to 6 digits may be problematic!
		auto s_iter = IO::int_to_formatted_string(iteration, 6);

		// Save current Image Chain
		auto imagesFile = this->chain->gneb_parameters->output_folder + "/" + starttime + "_Images_" + s_iter + suffix + ".txt";
		Utility::IO::Save_SpinChain_Configuration(this->chain, imagesFile);

		// Save current Energies with reaction coordinates
		auto energiesFile = this->chain->gneb_parameters->output_folder + "/" + starttime + "_E_Images_" + s_iter + suffix + ".txt";
		//		Check if Energy File exists and write Header if it doesn't
		std::ifstream f(energiesFile);
		if (!f.good()) Utility::IO::Write_Energy_Header(energiesFile);
		//		Save
		Utility::IO::Save_Energies(*this->chain, iteration, energiesFile);

		// Save interpolated Energies
		auto energiesInterpFile = this->chain->gneb_parameters->output_folder + "/" + starttime + "_E_interp_Images_" + s_iter + suffix + ".txt";
		Utility::IO::Save_Energies_Interpolated(*this->chain, energiesInterpFile);

		// Save Log
		Log.Append_to_File();
	}

	// Optimizer name as string
    std::string Method_GNEB::Name() { return "GNEB"; }
}<|endoftext|>


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

#include <gtest/gtest.h>
#include <capu/Config.h>

namespace capu
{
    TEST(ConfigTest, Types)
    {
        EXPECT_EQ(1u, sizeof(int8_t));
        EXPECT_EQ(1u, sizeof(int8_t));
        EXPECT_EQ(1u, sizeof(uint8_t));
        EXPECT_EQ(2u, sizeof(int16_t));
        EXPECT_EQ(2u, sizeof(uint16_t));
        EXPECT_EQ(4u, sizeof(int32_t));
        EXPECT_EQ(8u, sizeof(int64_t));
        EXPECT_EQ(4u, sizeof(uint32_t));
        EXPECT_EQ(8u, sizeof(uint64_t));
        EXPECT_EQ(4u, sizeof(float_t));
        EXPECT_EQ(8u, sizeof(double_t));
        EXPECT_EQ(1u, sizeof(bool_t));
        EXPECT_EQ(1u, sizeof(char_t));
        EXPECT_EQ(1u, sizeof(uchar_t));
#if defined (OS_MacOSX)
        EXPECT_EQ(4u, sizeof(time_t));
#elif defined (OS_WINDOWS)
        EXPECT_EQ(8u, sizeof(time_t));
#elif defined (ARCH_X86_32)
        EXPECT_EQ(4u, sizeof(time_t));
#elif defined (ARCH_X86_64)
        EXPECT_EQ(8u, sizeof(time_t));
#elif defined (ARCH_ARMV7L)
        EXPECT_EQ(4u, sizeof(time_t));
#endif

#if defined (ARCH_X86_32)
        EXPECT_EQ(4u, sizeof(int_t));
        EXPECT_EQ(4u, sizeof(uint_t));
#elif defined (ARCH_X86_64)
        EXPECT_EQ(8u, sizeof(int_t));
        EXPECT_EQ(8u, sizeof(uint_t));
#elif defined (ARCH_ARMV7L)
        EXPECT_EQ(4u, sizeof(int_t));
        EXPECT_EQ(4u, sizeof(uint_t));
#endif
    }
}
<commit_msg>Removing special treatment of MacOSX in ConfigTest<commit_after>/*
 * Copyright (C) 2012 BMW Car IT GmbH
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <gtest/gtest.h>
#include <capu/Config.h>

namespace capu
{
    TEST(ConfigTest, Types)
    {
        EXPECT_EQ(1u, sizeof(int8_t));
        EXPECT_EQ(1u, sizeof(int8_t));
        EXPECT_EQ(1u, sizeof(uint8_t));
        EXPECT_EQ(2u, sizeof(int16_t));
        EXPECT_EQ(2u, sizeof(uint16_t));
        EXPECT_EQ(4u, sizeof(int32_t));
        EXPECT_EQ(8u, sizeof(int64_t));
        EXPECT_EQ(4u, sizeof(uint32_t));
        EXPECT_EQ(8u, sizeof(uint64_t));
        EXPECT_EQ(4u, sizeof(float_t));
        EXPECT_EQ(8u, sizeof(double_t));
        EXPECT_EQ(1u, sizeof(bool_t));
        EXPECT_EQ(1u, sizeof(char_t));
        EXPECT_EQ(1u, sizeof(uchar_t));

#if defined (OS_WINDOWS)
        EXPECT_EQ(8u, sizeof(time_t));
#elif defined (ARCH_X86_32)
        EXPECT_EQ(4u, sizeof(time_t));
#elif defined (ARCH_X86_64)
        EXPECT_EQ(8u, sizeof(time_t));
#elif defined (ARCH_ARMV7L)
        EXPECT_EQ(4u, sizeof(time_t));
#endif

#if defined (ARCH_X86_32)
        EXPECT_EQ(4u, sizeof(int_t));
        EXPECT_EQ(4u, sizeof(uint_t));
#elif defined (ARCH_X86_64)
        EXPECT_EQ(8u, sizeof(int_t));
        EXPECT_EQ(8u, sizeof(uint_t));
#elif defined (ARCH_ARMV7L)
        EXPECT_EQ(4u, sizeof(int_t));
        EXPECT_EQ(4u, sizeof(uint_t));
#endif
    }
}
<|endoftext|>


<commit_before>/*********************************************************************************
 *
 * Inviwo - Interactive Visualization Workshop
 *
 * Copyright (c) 2020 Inviwo Foundation
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *********************************************************************************/

#include <modules/glfw/filewatcher.h>

#include <inviwo/core/common/inviwoapplication.h>
#include <inviwo/core/util/stringconversion.h>
#include <inviwo/core/util/logcentral.h>
#include <inviwo/core/util/zip.h>
#include <inviwo/core/util/fileobserver.h>
#include <inviwo/core/util/assertion.h>
#include <inviwo/core/util/stdextensions.h>
#include <inviwo/core/util/filesystem.h>
#include <inviwo/core/util/threadutil.h>

#include <array>
#include <mutex>
#include <atomic>
#include <chrono>
#include <algorithm>

#ifdef WIN32
#include <Windows.h>
#endif

namespace inviwo {

#ifdef WIN32

class WatcherThread {
public:
    enum class Action { Added, Removed, Modified };

    WatcherThread(
        std::function<void(const std::string&, const std::string&, Action)> changeCallback)
        : changeCallback_{std::move(changeCallback)} {

        util::setThreadDescription(thread_, "Inviwo File Watcher Thread");
    }

    ~WatcherThread() {
        stop_ = true;
        thread_.join();
    }

    bool addObservation(const std::string& path) {
        std::scoped_lock lock{mutex_};
        if (active_ + toAdd_.size() - toRemove_.size() + 1 < MAXIMUM_WAIT_OBJECTS) {
            toAdd_.push_back(path);
            return true;
        } else {
            return false;
        }
    }
    void removeObservation(const std::string& path) {
        std::scoped_lock lock{mutex_};
        toRemove_.push_back(path);
    }

private:
    void remove(const std::vector<std::string>& toRemove) {
        auto range = util::zip(handles_, observed_);
        auto it = std::remove_if(range.begin(), range.end(), [&](auto&& elem) {
            return std::find(toRemove.begin(), toRemove.end(), elem.second().first) !=
                   toRemove.end();
        });
        for (auto&& [handle, observed] : util::as_range(it, range.end())) {
            FindCloseChangeNotification(handle);
            observed.first.clear();
            observed.second.clear();
            --active_;
        }
    }

    void add(const std::vector<std::string>& toAdd) {
        for (auto& path : toAdd) {
            auto wpath = util::toWstring(path);
            const auto handle = FindFirstChangeNotification(wpath.c_str(), TRUE, filter);

            if (handle == INVALID_HANDLE_VALUE || handle == nullptr) {
                LogError("FindFirstChangeNotification function failed.");
                continue;
            }

            handles_[active_] = handle;
            observed_[active_].first = path;
            for (auto& elem : filesystem::getDirectoryContentsRecursively(
                     path, filesystem::ListMode::FilesAndDirectories)) {
                observed_[active_].second[elem] = filesystem::fileModificationTime(elem);
            }

            ++active_;
        }
    }

    void watch() {
        while (!stop_) {
            {
                std::scoped_lock lock{mutex_};
                if (!toRemove_.empty()) {
                    remove(toRemove_);
                    toRemove_.clear();
                }
                if (!toAdd_.empty()) {
                    add(toAdd_);
                    toAdd_.clear();
                }
            }
            if (active_ == 0) {
                std::this_thread::sleep_for(timeout_);
            } else {
                const auto status =
                    WaitForMultipleObjects(static_cast<DWORD>(active_), handles_.data(), FALSE,
                                           static_cast<DWORD>(timeout_.count()));
                if (status >= WAIT_OBJECT_0 && status < WAIT_OBJECT_0 + MAXIMUM_WAIT_OBJECTS) {

                    const auto& path = observed_[status - WAIT_OBJECT_0].first;
                    auto& files = observed_[status - WAIT_OBJECT_0].second;

                    const auto changedFiles = getChangedAndUpdateFiles(path, files);
                    for (auto&& [changedFile, action] : changedFiles) {
                        changeCallback_(path, changedFile, action);
                    }

                    const auto handle = handles_[status - WAIT_OBJECT_0];
                    FindNextChangeNotification(handle);
                }
            }
        }
        for (auto handle : util::as_range(handles_.begin(), handles_.begin() + active_)) {
            FindCloseChangeNotification(handle);
        }
    }

    std::vector<std::pair<std::string, Action>> getChangedAndUpdateFiles(
        const std::string& path, std::unordered_map<std::string, time_t>& files) {

        std::vector<std::pair<std::string, Action>> changed;

        util::map_erase_remove_if(files, [&](auto& item) {
            if (!filesystem::fileExists(item.first)) {
                changed.emplace_back(item.first, Action::Removed);
                return true;
            } else {
                return false;
            }
        });

        for (auto& elem : filesystem::getDirectoryContentsRecursively(
                 path, filesystem::ListMode::FilesAndDirectories)) {

            auto it = files.find(elem);
            if (it == files.end()) {
                changed.emplace_back(elem, Action::Removed);
                files[elem] = filesystem::fileModificationTime(elem);
            } else {
                auto newTime = filesystem::fileModificationTime(elem);
                if (newTime > it->second) {
                    changed.emplace_back(elem, Action::Modified);
                    it->second = newTime;
                }
            }
        }

        return changed;
    }

    static constexpr DWORD filter =
        FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_LAST_WRITE;

    std::array<HANDLE, MAXIMUM_WAIT_OBJECTS> handles_{};
    std::array<std::pair<std::string, std::unordered_map<std::string, time_t>>,
               MAXIMUM_WAIT_OBJECTS>
        observed_{};
    std::atomic<size_t> active_ = 0;

    std::function<void(const std::string&, const std::string&, Action)> changeCallback_;
    std::mutex mutex_;
    std::vector<std::string> toAdd_;
    std::vector<std::string> toRemove_;
    std::atomic<bool> stop_{false};
    std::chrono::milliseconds timeout_{1000};
    std::thread thread_{[this]() { watch(); }};
};

FileWatcher::FileWatcher(InviwoApplication* app)
    : app_{app}
    , watcher_{std::make_unique<WatcherThread>(
          [this](const std::string& dir, const std::string& path, WatcherThread::Action) {
              auto notifyAboutChanges = [this, dir, path]() {
                  if (filesystem::fileExists(path)) {
                      // don't use iterators here, they might be invalidated.
                      const auto orgSize = fileObservers_.size();
                      for (size_t i = 0; i < orgSize && i < fileObservers_.size(); ++i) {
                          if (fileObservers_[i]->isObserved(path)) {
                              fileObservers_[i]->fileChanged(path);
                          }
                      }
                  }
                  if (filesystem::directoryExists(dir)) {
                      // don't use iterators here, they might be invalidated.
                      const auto orgSize = fileObservers_.size();
                      for (size_t i = 0; i < orgSize && i < fileObservers_.size(); ++i) {
                          if (fileObservers_[i]->isObserved(dir)) {
                              fileObservers_[i]->fileChanged(dir);
                          }
                      }
                  }
              };

              if (app_) {
                  app_->dispatchFront(notifyAboutChanges);
              } else {
                  notifyAboutChanges();
              }
          })} {}

FileWatcher::~FileWatcher() = default;

void FileWatcher::registerFileObserver(FileObserver* fileObserver) {
    IVW_ASSERT(std::find(fileObservers_.cbegin(), fileObservers_.cend(), fileObserver) ==
                   fileObservers_.cend(),
               "File observer already registered.");
    fileObservers_.push_back(fileObserver);
}

void FileWatcher::unRegisterFileObserver(FileObserver* fileObserver) {
    const auto it = std::find(fileObservers_.begin(), fileObservers_.end(), fileObserver);
    if (it != fileObservers_.end()) {
        fileObservers_.erase(it);
    }
}

void FileWatcher::startFileObservation(const std::string& fileName) {
    const bool isDirectory = filesystem::directoryExists(fileName);
    const auto dir = isDirectory ? fileName : filesystem::getFileDirectory(fileName);

    const auto it = observed_.find(dir);
    if (it == observed_.end()) {
        observed_[dir].insert(fileName);
        if (!watcher_->addObservation(dir)) {
            LogError("Can't watch more files");
        }
    } else {
        it->second.insert(fileName);
    }
}

void FileWatcher::stopFileObservation(const std::string& fileName) {
    auto observerit =
        std::find_if(std::begin(fileObservers_), std::end(fileObservers_),
                     [fileName](const auto observer) { return observer->isObserved(fileName); });
    // Make sure that no observer is observing the file
    if (observerit == std::end(fileObservers_)) {
        const bool isDirectory = filesystem::directoryExists(fileName);
        const auto dir = isDirectory ? fileName : filesystem::getFileDirectory(fileName);

        const auto it = observed_.find(dir);
        if (it != observed_.end()) {
            it->second.erase(fileName);
            if (it->second.empty()) {
                watcher_->removeObservation(dir);
                observed_.erase(it);
            }
        }
    }
}

#else

class WatcherThread {
public:
    WatcherThread() = default;
};

FileWatcher::FileWatcher(InviwoApplication* app) : app_{app} {
    (void) app_;
    LogWarn("FileObserver are currently not supported using GLFW on this platform");
}

FileWatcher::~FileWatcher() = default;

void FileWatcher::registerFileObserver(FileObserver* fileObserver) {
    IVW_ASSERT(std::find(fileObservers_.cbegin(), fileObservers_.cend(), fileObserver) ==
                   fileObservers_.cend(),
               "File observer already registered.");
    fileObservers_.push_back(fileObserver);
}

void FileWatcher::unRegisterFileObserver(FileObserver* fileObserver) {
    util::erase_remove(fileObservers_, fileObserver);
}

void FileWatcher::stopFileObservation(const std::string& fileName) {}
void FileWatcher::startFileObservation(const std::string& fileName) {}

#endif

}  // namespace inviwo
<commit_msg>Jenkins: Format fixes<commit_after>/*********************************************************************************
 *
 * Inviwo - Interactive Visualization Workshop
 *
 * Copyright (c) 2020 Inviwo Foundation
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *********************************************************************************/

#include <modules/glfw/filewatcher.h>

#include <inviwo/core/common/inviwoapplication.h>
#include <inviwo/core/util/stringconversion.h>
#include <inviwo/core/util/logcentral.h>
#include <inviwo/core/util/zip.h>
#include <inviwo/core/util/fileobserver.h>
#include <inviwo/core/util/assertion.h>
#include <inviwo/core/util/stdextensions.h>
#include <inviwo/core/util/filesystem.h>
#include <inviwo/core/util/threadutil.h>

#include <array>
#include <mutex>
#include <atomic>
#include <chrono>
#include <algorithm>

#ifdef WIN32
#include <Windows.h>
#endif

namespace inviwo {

#ifdef WIN32

class WatcherThread {
public:
    enum class Action { Added, Removed, Modified };

    WatcherThread(
        std::function<void(const std::string&, const std::string&, Action)> changeCallback)
        : changeCallback_{std::move(changeCallback)} {

        util::setThreadDescription(thread_, "Inviwo File Watcher Thread");
    }

    ~WatcherThread() {
        stop_ = true;
        thread_.join();
    }

    bool addObservation(const std::string& path) {
        std::scoped_lock lock{mutex_};
        if (active_ + toAdd_.size() - toRemove_.size() + 1 < MAXIMUM_WAIT_OBJECTS) {
            toAdd_.push_back(path);
            return true;
        } else {
            return false;
        }
    }
    void removeObservation(const std::string& path) {
        std::scoped_lock lock{mutex_};
        toRemove_.push_back(path);
    }

private:
    void remove(const std::vector<std::string>& toRemove) {
        auto range = util::zip(handles_, observed_);
        auto it = std::remove_if(range.begin(), range.end(), [&](auto&& elem) {
            return std::find(toRemove.begin(), toRemove.end(), elem.second().first) !=
                   toRemove.end();
        });
        for (auto&& [handle, observed] : util::as_range(it, range.end())) {
            FindCloseChangeNotification(handle);
            observed.first.clear();
            observed.second.clear();
            --active_;
        }
    }

    void add(const std::vector<std::string>& toAdd) {
        for (auto& path : toAdd) {
            auto wpath = util::toWstring(path);
            const auto handle = FindFirstChangeNotification(wpath.c_str(), TRUE, filter);

            if (handle == INVALID_HANDLE_VALUE || handle == nullptr) {
                LogError("FindFirstChangeNotification function failed.");
                continue;
            }

            handles_[active_] = handle;
            observed_[active_].first = path;
            for (auto& elem : filesystem::getDirectoryContentsRecursively(
                     path, filesystem::ListMode::FilesAndDirectories)) {
                observed_[active_].second[elem] = filesystem::fileModificationTime(elem);
            }

            ++active_;
        }
    }

    void watch() {
        while (!stop_) {
            {
                std::scoped_lock lock{mutex_};
                if (!toRemove_.empty()) {
                    remove(toRemove_);
                    toRemove_.clear();
                }
                if (!toAdd_.empty()) {
                    add(toAdd_);
                    toAdd_.clear();
                }
            }
            if (active_ == 0) {
                std::this_thread::sleep_for(timeout_);
            } else {
                const auto status =
                    WaitForMultipleObjects(static_cast<DWORD>(active_), handles_.data(), FALSE,
                                           static_cast<DWORD>(timeout_.count()));
                if (status >= WAIT_OBJECT_0 && status < WAIT_OBJECT_0 + MAXIMUM_WAIT_OBJECTS) {

                    const auto& path = observed_[status - WAIT_OBJECT_0].first;
                    auto& files = observed_[status - WAIT_OBJECT_0].second;

                    const auto changedFiles = getChangedAndUpdateFiles(path, files);
                    for (auto&& [changedFile, action] : changedFiles) {
                        changeCallback_(path, changedFile, action);
                    }

                    const auto handle = handles_[status - WAIT_OBJECT_0];
                    FindNextChangeNotification(handle);
                }
            }
        }
        for (auto handle : util::as_range(handles_.begin(), handles_.begin() + active_)) {
            FindCloseChangeNotification(handle);
        }
    }

    std::vector<std::pair<std::string, Action>> getChangedAndUpdateFiles(
        const std::string& path, std::unordered_map<std::string, time_t>& files) {

        std::vector<std::pair<std::string, Action>> changed;

        util::map_erase_remove_if(files, [&](auto& item) {
            if (!filesystem::fileExists(item.first)) {
                changed.emplace_back(item.first, Action::Removed);
                return true;
            } else {
                return false;
            }
        });

        for (auto& elem : filesystem::getDirectoryContentsRecursively(
                 path, filesystem::ListMode::FilesAndDirectories)) {

            auto it = files.find(elem);
            if (it == files.end()) {
                changed.emplace_back(elem, Action::Removed);
                files[elem] = filesystem::fileModificationTime(elem);
            } else {
                auto newTime = filesystem::fileModificationTime(elem);
                if (newTime > it->second) {
                    changed.emplace_back(elem, Action::Modified);
                    it->second = newTime;
                }
            }
        }

        return changed;
    }

    static constexpr DWORD filter =
        FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_LAST_WRITE;

    std::array<HANDLE, MAXIMUM_WAIT_OBJECTS> handles_{};
    std::array<std::pair<std::string, std::unordered_map<std::string, time_t>>,
               MAXIMUM_WAIT_OBJECTS>
        observed_{};
    std::atomic<size_t> active_ = 0;

    std::function<void(const std::string&, const std::string&, Action)> changeCallback_;
    std::mutex mutex_;
    std::vector<std::string> toAdd_;
    std::vector<std::string> toRemove_;
    std::atomic<bool> stop_{false};
    std::chrono::milliseconds timeout_{1000};
    std::thread thread_{[this]() { watch(); }};
};

FileWatcher::FileWatcher(InviwoApplication* app)
    : app_{app}
    , watcher_{std::make_unique<WatcherThread>(
          [this](const std::string& dir, const std::string& path, WatcherThread::Action) {
              auto notifyAboutChanges = [this, dir, path]() {
                  if (filesystem::fileExists(path)) {
                      // don't use iterators here, they might be invalidated.
                      const auto orgSize = fileObservers_.size();
                      for (size_t i = 0; i < orgSize && i < fileObservers_.size(); ++i) {
                          if (fileObservers_[i]->isObserved(path)) {
                              fileObservers_[i]->fileChanged(path);
                          }
                      }
                  }
                  if (filesystem::directoryExists(dir)) {
                      // don't use iterators here, they might be invalidated.
                      const auto orgSize = fileObservers_.size();
                      for (size_t i = 0; i < orgSize && i < fileObservers_.size(); ++i) {
                          if (fileObservers_[i]->isObserved(dir)) {
                              fileObservers_[i]->fileChanged(dir);
                          }
                      }
                  }
              };

              if (app_) {
                  app_->dispatchFront(notifyAboutChanges);
              } else {
                  notifyAboutChanges();
              }
          })} {}

FileWatcher::~FileWatcher() = default;

void FileWatcher::registerFileObserver(FileObserver* fileObserver) {
    IVW_ASSERT(std::find(fileObservers_.cbegin(), fileObservers_.cend(), fileObserver) ==
                   fileObservers_.cend(),
               "File observer already registered.");
    fileObservers_.push_back(fileObserver);
}

void FileWatcher::unRegisterFileObserver(FileObserver* fileObserver) {
    const auto it = std::find(fileObservers_.begin(), fileObservers_.end(), fileObserver);
    if (it != fileObservers_.end()) {
        fileObservers_.erase(it);
    }
}

void FileWatcher::startFileObservation(const std::string& fileName) {
    const bool isDirectory = filesystem::directoryExists(fileName);
    const auto dir = isDirectory ? fileName : filesystem::getFileDirectory(fileName);

    const auto it = observed_.find(dir);
    if (it == observed_.end()) {
        observed_[dir].insert(fileName);
        if (!watcher_->addObservation(dir)) {
            LogError("Can't watch more files");
        }
    } else {
        it->second.insert(fileName);
    }
}

void FileWatcher::stopFileObservation(const std::string& fileName) {
    auto observerit =
        std::find_if(std::begin(fileObservers_), std::end(fileObservers_),
                     [fileName](const auto observer) { return observer->isObserved(fileName); });
    // Make sure that no observer is observing the file
    if (observerit == std::end(fileObservers_)) {
        const bool isDirectory = filesystem::directoryExists(fileName);
        const auto dir = isDirectory ? fileName : filesystem::getFileDirectory(fileName);

        const auto it = observed_.find(dir);
        if (it != observed_.end()) {
            it->second.erase(fileName);
            if (it->second.empty()) {
                watcher_->removeObservation(dir);
                observed_.erase(it);
            }
        }
    }
}

#else

class WatcherThread {
public:
    WatcherThread() = default;
};

FileWatcher::FileWatcher(InviwoApplication* app) : app_{app} {
    (void)app_;
    LogWarn("FileObserver are currently not supported using GLFW on this platform");
}

FileWatcher::~FileWatcher() = default;

void FileWatcher::registerFileObserver(FileObserver* fileObserver) {
    IVW_ASSERT(std::find(fileObservers_.cbegin(), fileObservers_.cend(), fileObserver) ==
                   fileObservers_.cend(),
               "File observer already registered.");
    fileObservers_.push_back(fileObserver);
}

void FileWatcher::unRegisterFileObserver(FileObserver* fileObserver) {
    util::erase_remove(fileObservers_, fileObserver);
}

void FileWatcher::stopFileObservation(const std::string& fileName) {}
void FileWatcher::startFileObservation(const std::string& fileName) {}

#endif

}  // namespace inviwo
<|endoftext|>


<commit_before>#ifndef LISP_PTR_I_HH
#define LISP_PTR_I_HH

#ifndef LISP_PTR_HH
#error "Please include via parent file"
#endif

#include <cassert>
#include <type_traits>

// Lisp_ptr constructors
inline constexpr
Lisp_ptr::Lisp_ptr()
 : tag_(Ptr_tag::undefined), u_(0){}

inline constexpr
Lisp_ptr::Lisp_ptr(bool b)
  : tag_(to_tag<bool>()), u_(b){}

inline constexpr
Lisp_ptr::Lisp_ptr(char c)
  : tag_(to_tag<char>()), u_(c){}

inline constexpr
Lisp_ptr::Lisp_ptr(int i)
  : tag_(to_tag<int>()), u_(i){}

template<typename T>
inline constexpr
Lisp_ptr::Lisp_ptr(T p)
  : tag_(to_tag<T>()), u_(p){
  static_assert(!std::is_fundamental<T>::value,
                "Lisp_ptr cannot accept the specified type.");
}

template<>
inline constexpr
Lisp_ptr::Lisp_ptr<Notation>(Notation n)
  : tag_(to_tag<Notation>()), u_(static_cast<int>(n)){}

// Lisp_ptr getters
template<>
inline
VMArgcount Lisp_ptr::get<VMArgcount>() const {
  assert(tag() == to_tag<VMArgcount>());
  return static_cast<VMArgcount>(u_.i_);
}

template<>
inline
Notation Lisp_ptr::get<Notation>() const {
  assert(tag() == to_tag<Notation>());
  return static_cast<Notation>(u_.i_);
}

template<>
inline constexpr
intptr_t Lisp_ptr::get<intptr_t>() const{
  return u_.i_;
}

template<>
inline constexpr
void* Lisp_ptr::get<void*>() const{
  return u_.ptr_;
}

template<>
inline constexpr
const void* Lisp_ptr::get<const void*>() const{
  return u_.ptr_;
}

template<>
inline constexpr
void (*Lisp_ptr::get<void(*)(void)>())(void) const{
  return u_.f_;
}

namespace lisp_ptr_detail {

template<typename T>
inline constexpr
T lisp_ptr_cast(const Lisp_ptr& p,
                typename std::enable_if<std::is_integral<T>::value>::type* = nullptr){
  return static_cast<T>(p.get<intptr_t>());
}

template<typename T>
inline constexpr
T lisp_ptr_cast(const Lisp_ptr& p,
                typename std::enable_if<std::is_pointer<T>::value>::type* = nullptr,
                typename std::enable_if<
                  std::is_function<typename std::remove_pointer<T>::type>::value
                  >::type* = nullptr){
  static_assert(std::is_same<T, void(*)(void)>::value,
                "inacceptable function-pointer type");
  return static_cast<T>(p.get<void(*)(void)>());
}

template<typename T>
inline constexpr
T lisp_ptr_cast(const Lisp_ptr& p,
                typename std::enable_if<std::is_pointer<T>::value>::type* = nullptr,
                typename std::enable_if<
                  !std::is_function<typename std::remove_pointer<T>::type>::value
                  >::type* = nullptr,
                typename std::enable_if<
                  !std::is_const<typename std::remove_pointer<T>::type>::value
                  >::type* = nullptr){
  return static_cast<T>(p.get<void*>());
}

template<typename T>
inline constexpr
T lisp_ptr_cast(const Lisp_ptr& p,
                typename std::enable_if<std::is_pointer<T>::value>::type* = nullptr,
                typename std::enable_if<
                  !std::is_function<typename std::remove_pointer<T>::type>::value
                  >::type* = nullptr,
                typename std::enable_if<
                  std::is_const<typename std::remove_pointer<T>::type>::value
                  >::type* = nullptr){
  return static_cast<T>(p.get<const void*>());
}

}

template<typename T>
inline
T Lisp_ptr::get() const {
  static_assert(to_tag<T>() != Ptr_tag::undefined, "inacceptable type");
  assert(tag() == to_tag<T>());
  return lisp_ptr_detail::lisp_ptr_cast<T>(*this);
}

#endif // LISP_PTR_I_HH
<commit_msg>lisp_ptr can handle enum<commit_after>#ifndef LISP_PTR_I_HH
#define LISP_PTR_I_HH

#ifndef LISP_PTR_HH
#error "Please include via parent file"
#endif

#include <cassert>
#include <type_traits>

// Lisp_ptr constructors
inline constexpr
Lisp_ptr::Lisp_ptr()
 : tag_(Ptr_tag::undefined), u_(0){}

inline constexpr
Lisp_ptr::Lisp_ptr(bool b)
  : tag_(to_tag<bool>()), u_(b){}

inline constexpr
Lisp_ptr::Lisp_ptr(char c)
  : tag_(to_tag<char>()), u_(c){}

inline constexpr
Lisp_ptr::Lisp_ptr(int i)
  : tag_(to_tag<int>()), u_(i){}

template<typename T>
inline constexpr
Lisp_ptr::Lisp_ptr(T p)
  : tag_(to_tag<T>()), u_(p){
  static_assert(!std::is_fundamental<T>::value,
                "Lisp_ptr cannot accept the specified type.");
}

template<>
inline constexpr
Lisp_ptr::Lisp_ptr<Notation>(Notation n)
  : tag_(to_tag<Notation>()), u_(static_cast<int>(n)){}

// Lisp_ptr getters
template<>
inline
VMArgcount Lisp_ptr::get<VMArgcount>() const {
  assert(tag() == to_tag<VMArgcount>());
  return static_cast<VMArgcount>(u_.i_);
}

template<>
inline constexpr
intptr_t Lisp_ptr::get<intptr_t>() const{
  return u_.i_;
}

template<>
inline constexpr
void* Lisp_ptr::get<void*>() const{
  return u_.ptr_;
}

template<>
inline constexpr
const void* Lisp_ptr::get<const void*>() const{
  return u_.cptr_;
}

template<>
inline constexpr
void (*Lisp_ptr::get<void(*)(void)>())(void) const{
  return u_.f_;
}

namespace lisp_ptr_detail {

template<typename T>
inline constexpr
T lisp_ptr_cast(const Lisp_ptr& p,
                typename std::enable_if<std::is_integral<T>::value>::type* = nullptr){
  return static_cast<T>(p.get<intptr_t>());
}

template<typename T>
inline constexpr
T lisp_ptr_cast(const Lisp_ptr& p,
                typename std::enable_if<std::is_enum<T>::value>::type* = nullptr){
  // GCC 4.6 cannot accept below..
  // static_assert(std::is_convertible<typename std::underlying_type<T>::type,
  //                                   intptr_t>::value,
  //               "inacceptable enum type");
  return static_cast<T>(p.get<intptr_t>());
}

template<typename T>
inline constexpr
T lisp_ptr_cast(const Lisp_ptr& p,
                typename std::enable_if<std::is_pointer<T>::value>::type* = nullptr,
                typename std::enable_if<
                  std::is_function<typename std::remove_pointer<T>::type>::value
                  >::type* = nullptr){
  static_assert(std::is_same<T, void(*)(void)>::value,
                "inacceptable function-pointer type");
  return static_cast<T>(p.get<void(*)(void)>());
}

template<typename T>
inline constexpr
T lisp_ptr_cast(const Lisp_ptr& p,
                typename std::enable_if<std::is_pointer<T>::value>::type* = nullptr,
                typename std::enable_if<
                  !std::is_function<typename std::remove_pointer<T>::type>::value
                  >::type* = nullptr,
                typename std::enable_if<
                  !std::is_const<typename std::remove_pointer<T>::type>::value
                  >::type* = nullptr){
  return static_cast<T>(p.get<void*>());
}

template<typename T>
inline constexpr
T lisp_ptr_cast(const Lisp_ptr& p,
                typename std::enable_if<std::is_pointer<T>::value>::type* = nullptr,
                typename std::enable_if<
                  !std::is_function<typename std::remove_pointer<T>::type>::value
                  >::type* = nullptr,
                typename std::enable_if<
                  std::is_const<typename std::remove_pointer<T>::type>::value
                  >::type* = nullptr){
  return static_cast<T>(p.get<const void*>());
}

}

template<typename T>
inline
T Lisp_ptr::get() const {
  static_assert(to_tag<T>() != Ptr_tag::undefined, "inacceptable type");
  assert(tag() == to_tag<T>());
  return lisp_ptr_detail::lisp_ptr_cast<T>(*this);
}

#endif // LISP_PTR_I_HH
<|endoftext|>


<commit_before>#include <libmesh/parallel_implementation.h> // max()
#include <libmesh/elem.h>
#include <libmesh/mesh.h>
#include <libmesh/mesh_triangle_holes.h>
#include <libmesh/mesh_triangle_interface.h>
#include <libmesh/point.h>
#include <libmesh/poly2tri_triangulator.h>

#include "test_comm.h"
#include "libmesh_cppunit.h"

#include <cmath>

using namespace libMesh;

class MeshTriangulationTest : public CppUnit::TestCase
{
  /**
   * The goal of this test is to verify proper operation of the
   * interfaces to triangulation libraries
   */
public:
  CPPUNIT_TEST_SUITE( MeshTriangulationTest );

  CPPUNIT_TEST( testTriangleHoleArea );

#ifdef LIBMESH_HAVE_POLY2TRI
  CPPUNIT_TEST( testPoly2Tri );
  CPPUNIT_TEST( testPoly2TriHoles );
  CPPUNIT_TEST( testPoly2TriSegments );
  CPPUNIT_TEST( testPoly2TriRefined );
#endif

#ifdef LIBMESH_HAVE_TRIANGLE
  CPPUNIT_TEST( testTriangle );
  CPPUNIT_TEST( testTriangleHoles );
  CPPUNIT_TEST( testTriangleSegments );
#endif

  CPPUNIT_TEST_SUITE_END();

public:
  void setUp() {}

  void tearDown() {}

  void testTriangleHoleArea()
  {
    // Using center=(1,0), radius=2 for the heck of it
    Point center{1};
    Real radius = 2;
    std::vector<TriangulatorInterface::PolygonHole> polyholes;

    // Line
    polyholes.emplace_back(center, radius, 2);
    // Triangle
    polyholes.emplace_back(center, radius, 3);
    // Square
    polyholes.emplace_back(center, radius, 4);
    // Pentagon
    polyholes.emplace_back(center, radius, 5);
    // Hexagon
    polyholes.emplace_back(center, radius, 6);

    for (int i=0; i != 5; ++i)
      {
        const int n_sides = i+2;
        const TriangulatorInterface::Hole & hole = polyholes[i];

        // Really?  This isn't until C++20?
        constexpr double my_pi = 3.141592653589793238462643383279;

        const Real computed_area = hole.area();
        const Real theta = my_pi/n_sides;
        const Real half_side_length = radius*std::cos(theta);
        const Real apothem = radius*std::sin(theta);
        const Real area = n_sides * apothem * half_side_length;

        LIBMESH_ASSERT_FP_EQUAL(computed_area, area, TOLERANCE*TOLERANCE);
      }

    TriangulatorInterface::ArbitraryHole arbhole {center, {{0,-1},{2,-1},{2,1},{0,2}}};
    LIBMESH_ASSERT_FP_EQUAL(arbhole.area(), Real(5), TOLERANCE*TOLERANCE);

#ifdef LIBMESH_HAVE_TRIANGLE
    // Make sure we're compatible with the old naming structure too
    TriangleInterface::PolygonHole square(center, radius, 4);
    LIBMESH_ASSERT_FP_EQUAL(square.area(), 2*radius*radius, TOLERANCE*TOLERANCE);
#endif
  }

  void testTriangulatorBase(MeshBase & mesh,
                            TriangulatorInterface & triangulator)
  {
    // Use the point order to define the boundary, because our
    // Poly2Tri implementation doesn't do convex hulls yet, even when
    // that would give the same answer.
    triangulator.triangulation_type() = TriangulatorInterface::PSLG;

    // Don't try to insert points yet
    triangulator.desired_area() = 1000;
    triangulator.minimum_angle() = 0;
    triangulator.smooth_after_generating() = false;

    triangulator.triangulate();

    CPPUNIT_ASSERT_EQUAL(mesh.n_elem(), dof_id_type(2));
    for (const auto & elem : mesh.element_ptr_range())
      {
        CPPUNIT_ASSERT_EQUAL(elem->type(), TRI3);

        // Make sure we're not getting any inverted elements
        auto cross_prod =
          (elem->point(1) - elem->point(0)).cross
            (elem->point(2) - elem->point(0));

        CPPUNIT_ASSERT_GREATER(Real(0), cross_prod(2));

        bool found_triangle = false;
        for (const auto & node : elem->node_ref_range())
          {
            const Point & point = node;
            if (point == Point(0,0))
              {
                found_triangle = true;
                CPPUNIT_ASSERT((elem->point(0) == Point(0,0) &&
                                elem->point(1) == Point(1,0) &&
                                elem->point(2) == Point(0,1)) ||
                               (elem->point(1) == Point(0,0) &&
                                elem->point(2) == Point(1,0) &&
                                elem->point(0) == Point(0,1)) ||
                               (elem->point(2) == Point(0,0) &&
                                elem->point(0) == Point(1,0) &&
                                elem->point(1) == Point(0,1)));
              }
            if (point == Point(1,2))
              {
                found_triangle = true;
                CPPUNIT_ASSERT((elem->point(0) == Point(0,1) &&
                                elem->point(1) == Point(1,0) &&
                                elem->point(2) == Point(1,2)) ||
                               (elem->point(1) == Point(0,1) &&
                                elem->point(2) == Point(1,0) &&
                                elem->point(0) == Point(1,2)) ||
                               (elem->point(2) == Point(0,1) &&
                                elem->point(0) == Point(1,0) &&
                                elem->point(1) == Point(1,2)));
              }
          }
        CPPUNIT_ASSERT(found_triangle);
      }
  }

  void testTriangulator(MeshBase & mesh,
                        TriangulatorInterface & triangulator)
  {
    // A non-square quad, so we don't have ambiguity about which
    // diagonal a Delaunay algorithm will pick.
    mesh.add_point(Point(0,0));
    mesh.add_point(Point(1,0));
    mesh.add_point(Point(1,2));
    mesh.add_point(Point(0,1));

    this->testTriangulatorBase(mesh, triangulator);
  }

  void testTriangulatorHoles(MeshBase & mesh,
                             TriangulatorInterface & triangulator)
  {
    // A square quad; we'll put a diamond hole in the middle to make
    // the Delaunay selection unambiguous.
    mesh.add_point(Point(-1,-1));
    mesh.add_point(Point(1,-1));
    mesh.add_point(Point(1,1));
    mesh.add_point(Point(-1,1));

    // Use the point order to define the boundary, because our
    // Poly2Tri implementation doesn't do convex hulls yet, even when
    // that would give the same answer.
    triangulator.triangulation_type() = TriangulatorInterface::PSLG;

    // Add a diamond hole in the center
    TriangulatorInterface::PolygonHole diamond(Point(0), std::sqrt(2)/2, 4);
    const std::vector<TriangulatorInterface::Hole*> holes { &diamond };
    triangulator.attach_hole_list(&holes);

    // Don't try to insert points yet
    triangulator.desired_area() = 1000;
    triangulator.minimum_angle() = 0;
    triangulator.smooth_after_generating() = false;

    triangulator.triangulate();

    CPPUNIT_ASSERT_EQUAL(mesh.n_elem(), dof_id_type(8));

    // Center coordinates for all the elements we expect
    Real r2p2o6 = (std::sqrt(Real(2))+2)/6;
    Real r2p4o6 = (std::sqrt(Real(2))+4)/6;

    std::vector <Point> expected_centers
    { {r2p2o6,r2p2o6}, {r2p2o6,-r2p2o6},
      {-r2p2o6,r2p2o6}, {-r2p2o6,-r2p2o6},
      {0,r2p4o6}, {r2p4o6, 0},
      {0,-r2p4o6}, {-r2p4o6, 0}
    };

    std::vector<bool> found_centers(expected_centers.size(), false);

    for (const auto & elem : mesh.element_ptr_range())
      {
        CPPUNIT_ASSERT_EQUAL(elem->type(), TRI3);

        // Make sure we're not getting any inverted elements
        auto cross_prod =
          (elem->point(1) - elem->point(0)).cross
            (elem->point(2) - elem->point(0));

        CPPUNIT_ASSERT_GREATER(Real(0), cross_prod(2));

        // Make sure we're finding all the elements we expect
        Point center = elem->vertex_average();

        bool found_mine = false;
        for (auto i : index_range(expected_centers))
          {
            Point possible = expected_centers[i];

            if (possible.absolute_fuzzy_equals(center, TOLERANCE*TOLERANCE))
              {
                found_mine = true;
                found_centers[i] = true;
              }
          }
        CPPUNIT_ASSERT(found_mine);
      }

    mesh.comm().max(found_centers);

    for (auto found_it : found_centers)
      CPPUNIT_ASSERT(found_it);
  }

  void testTriangulatorSegments(MeshBase & mesh,
                                TriangulatorInterface & triangulator)
  {
    // The same quad as testTriangulator, but out of order
    mesh.add_point(Point(0,0));
    mesh.add_point(Point(1,2));
    mesh.add_point(Point(1,0));
    mesh.add_point(Point(0,1));

    // Segments to put them in order
    triangulator.segments = {{0,2},{2,1},{1,3},{3,0}};

    this->testTriangulatorBase(mesh, triangulator);
  }

#ifdef LIBMESH_HAVE_TRIANGLE
  void testTriangle()
  {
    Mesh mesh(*TestCommWorld);
    TriangleInterface triangle(mesh);
    testTriangulator(mesh, triangle);
  }

  void testTriangleHoles()
  {
    Mesh mesh(*TestCommWorld);
    TriangleInterface triangle(mesh);
    testTriangulatorHoles(mesh, triangle);
  }

  void testTriangleSegments()
  {
    Mesh mesh(*TestCommWorld);
    TriangleInterface triangle(mesh);
    testTriangulatorSegments(mesh, triangle);
  }
#endif // LIBMESH_HAVE_TRIANGLE

#ifdef LIBMESH_HAVE_POLY2TRI
  void testPoly2Tri()
  {
    Mesh mesh(*TestCommWorld);
    Poly2TriTriangulator p2t_tri(mesh);
    testTriangulator(mesh, p2t_tri);
  }

  void testPoly2TriHoles()
  {
    Mesh mesh(*TestCommWorld);
    Poly2TriTriangulator p2t_tri(mesh);
    testTriangulatorHoles(mesh, p2t_tri);
  }

  void testPoly2TriSegments()
  {
    Mesh mesh(*TestCommWorld);
    Poly2TriTriangulator p2t_tri(mesh);
    testTriangulatorSegments(mesh, p2t_tri);
  }

  void testPoly2TriRefined()
  {
    Mesh mesh(*TestCommWorld);
    mesh.add_point(Point(0,0));
    mesh.add_point(Point(1,0));
    mesh.add_point(Point(1,2));
    mesh.add_point(Point(0,1));

    Poly2TriTriangulator triangulator(mesh);

    // Use the point order to define the boundary, because our
    // Poly2Tri implementation doesn't do convex hulls yet, even when
    // that would give the same answer.
    triangulator.triangulation_type() = TriangulatorInterface::PSLG;

    // Try to insert points!
    triangulator.desired_area() = 0.1;
    triangulator.minimum_angle() = 0;
    triangulator.smooth_after_generating() = false;

    triangulator.triangulate();

    CPPUNIT_ASSERT(mesh.n_elem() > 2);

    Real area = 0;
    for (const auto & elem : mesh.element_ptr_range())
      {
        CPPUNIT_ASSERT_EQUAL(elem->type(), TRI3);

        area += elem->volume();
      }

    LIBMESH_ASSERT_FP_EQUAL(area, 1.5, TOLERANCE*TOLERANCE);
  }
#endif // LIBMESH_HAVE_POLY2TRI

};

CPPUNIT_TEST_SUITE_REGISTRATION( MeshTriangulationTest );
<commit_msg>Explicit numbering in triangulation tests<commit_after>#include <libmesh/parallel_implementation.h> // max()
#include <libmesh/elem.h>
#include <libmesh/mesh.h>
#include <libmesh/mesh_triangle_holes.h>
#include <libmesh/mesh_triangle_interface.h>
#include <libmesh/point.h>
#include <libmesh/poly2tri_triangulator.h>

#include "test_comm.h"
#include "libmesh_cppunit.h"

#include <cmath>

using namespace libMesh;

class MeshTriangulationTest : public CppUnit::TestCase
{
  /**
   * The goal of this test is to verify proper operation of the
   * interfaces to triangulation libraries
   */
public:
  CPPUNIT_TEST_SUITE( MeshTriangulationTest );

  CPPUNIT_TEST( testTriangleHoleArea );

#ifdef LIBMESH_HAVE_POLY2TRI
  CPPUNIT_TEST( testPoly2Tri );
  CPPUNIT_TEST( testPoly2TriHoles );
  CPPUNIT_TEST( testPoly2TriSegments );
  CPPUNIT_TEST( testPoly2TriRefined );
#endif

#ifdef LIBMESH_HAVE_TRIANGLE
  CPPUNIT_TEST( testTriangle );
  CPPUNIT_TEST( testTriangleHoles );
  CPPUNIT_TEST( testTriangleSegments );
#endif

  CPPUNIT_TEST_SUITE_END();

public:
  void setUp() {}

  void tearDown() {}

  void testTriangleHoleArea()
  {
    // Using center=(1,0), radius=2 for the heck of it
    Point center{1};
    Real radius = 2;
    std::vector<TriangulatorInterface::PolygonHole> polyholes;

    // Line
    polyholes.emplace_back(center, radius, 2);
    // Triangle
    polyholes.emplace_back(center, radius, 3);
    // Square
    polyholes.emplace_back(center, radius, 4);
    // Pentagon
    polyholes.emplace_back(center, radius, 5);
    // Hexagon
    polyholes.emplace_back(center, radius, 6);

    for (int i=0; i != 5; ++i)
      {
        const int n_sides = i+2;
        const TriangulatorInterface::Hole & hole = polyholes[i];

        // Really?  This isn't until C++20?
        constexpr double my_pi = 3.141592653589793238462643383279;

        const Real computed_area = hole.area();
        const Real theta = my_pi/n_sides;
        const Real half_side_length = radius*std::cos(theta);
        const Real apothem = radius*std::sin(theta);
        const Real area = n_sides * apothem * half_side_length;

        LIBMESH_ASSERT_FP_EQUAL(computed_area, area, TOLERANCE*TOLERANCE);
      }

    TriangulatorInterface::ArbitraryHole arbhole {center, {{0,-1},{2,-1},{2,1},{0,2}}};
    LIBMESH_ASSERT_FP_EQUAL(arbhole.area(), Real(5), TOLERANCE*TOLERANCE);

#ifdef LIBMESH_HAVE_TRIANGLE
    // Make sure we're compatible with the old naming structure too
    TriangleInterface::PolygonHole square(center, radius, 4);
    LIBMESH_ASSERT_FP_EQUAL(square.area(), 2*radius*radius, TOLERANCE*TOLERANCE);
#endif
  }

  void testTriangulatorBase(MeshBase & mesh,
                            TriangulatorInterface & triangulator)
  {
    // Use the point order to define the boundary, because our
    // Poly2Tri implementation doesn't do convex hulls yet, even when
    // that would give the same answer.
    triangulator.triangulation_type() = TriangulatorInterface::PSLG;

    // Don't try to insert points yet
    triangulator.desired_area() = 1000;
    triangulator.minimum_angle() = 0;
    triangulator.smooth_after_generating() = false;

    triangulator.triangulate();

    CPPUNIT_ASSERT_EQUAL(mesh.n_elem(), dof_id_type(2));
    for (const auto & elem : mesh.element_ptr_range())
      {
        CPPUNIT_ASSERT_EQUAL(elem->type(), TRI3);

        // Make sure we're not getting any inverted elements
        auto cross_prod =
          (elem->point(1) - elem->point(0)).cross
            (elem->point(2) - elem->point(0));

        CPPUNIT_ASSERT_GREATER(Real(0), cross_prod(2));

        bool found_triangle = false;
        for (const auto & node : elem->node_ref_range())
          {
            const Point & point = node;
            if (point == Point(0,0))
              {
                found_triangle = true;
                CPPUNIT_ASSERT((elem->point(0) == Point(0,0) &&
                                elem->point(1) == Point(1,0) &&
                                elem->point(2) == Point(0,1)) ||
                               (elem->point(1) == Point(0,0) &&
                                elem->point(2) == Point(1,0) &&
                                elem->point(0) == Point(0,1)) ||
                               (elem->point(2) == Point(0,0) &&
                                elem->point(0) == Point(1,0) &&
                                elem->point(1) == Point(0,1)));
              }
            if (point == Point(1,2))
              {
                found_triangle = true;
                CPPUNIT_ASSERT((elem->point(0) == Point(0,1) &&
                                elem->point(1) == Point(1,0) &&
                                elem->point(2) == Point(1,2)) ||
                               (elem->point(1) == Point(0,1) &&
                                elem->point(2) == Point(1,0) &&
                                elem->point(0) == Point(1,2)) ||
                               (elem->point(2) == Point(0,1) &&
                                elem->point(0) == Point(1,0) &&
                                elem->point(1) == Point(1,2)));
              }
          }
        CPPUNIT_ASSERT(found_triangle);
      }
  }

  void testTriangulator(MeshBase & mesh,
                        TriangulatorInterface & triangulator)
  {
    // A non-square quad, so we don't have ambiguity about which
    // diagonal a Delaunay algorithm will pick.
    // Manually-numbered points, so we can use the point numbering as
    // a segment ordering even on DistributedMesh.
    mesh.add_point(Point(0,0), 0);
    mesh.add_point(Point(1,0), 1);
    mesh.add_point(Point(1,2), 2);
    mesh.add_point(Point(0,1), 3);

    this->testTriangulatorBase(mesh, triangulator);
  }

  void testTriangulatorHoles(MeshBase & mesh,
                             TriangulatorInterface & triangulator)
  {
    // A square quad; we'll put a diamond hole in the middle to make
    // the Delaunay selection unambiguous.
    mesh.add_point(Point(-1,-1), 0);
    mesh.add_point(Point(1,-1), 1);
    mesh.add_point(Point(1,1), 2);
    mesh.add_point(Point(-1,1), 3);

    // Use the point order to define the boundary, because our
    // Poly2Tri implementation doesn't do convex hulls yet, even when
    // that would give the same answer.
    triangulator.triangulation_type() = TriangulatorInterface::PSLG;

    // Add a diamond hole in the center
    TriangulatorInterface::PolygonHole diamond(Point(0), std::sqrt(2)/2, 4);
    const std::vector<TriangulatorInterface::Hole*> holes { &diamond };
    triangulator.attach_hole_list(&holes);

    // Don't try to insert points yet
    triangulator.desired_area() = 1000;
    triangulator.minimum_angle() = 0;
    triangulator.smooth_after_generating() = false;

    triangulator.triangulate();

    CPPUNIT_ASSERT_EQUAL(mesh.n_elem(), dof_id_type(8));

    // Center coordinates for all the elements we expect
    Real r2p2o6 = (std::sqrt(Real(2))+2)/6;
    Real r2p4o6 = (std::sqrt(Real(2))+4)/6;

    std::vector <Point> expected_centers
    { {r2p2o6,r2p2o6}, {r2p2o6,-r2p2o6},
      {-r2p2o6,r2p2o6}, {-r2p2o6,-r2p2o6},
      {0,r2p4o6}, {r2p4o6, 0},
      {0,-r2p4o6}, {-r2p4o6, 0}
    };

    std::vector<bool> found_centers(expected_centers.size(), false);

    for (const auto & elem : mesh.element_ptr_range())
      {
        CPPUNIT_ASSERT_EQUAL(elem->type(), TRI3);

        // Make sure we're not getting any inverted elements
        auto cross_prod =
          (elem->point(1) - elem->point(0)).cross
            (elem->point(2) - elem->point(0));

        CPPUNIT_ASSERT_GREATER(Real(0), cross_prod(2));

        // Make sure we're finding all the elements we expect
        Point center = elem->vertex_average();

        bool found_mine = false;
        for (auto i : index_range(expected_centers))
          {
            Point possible = expected_centers[i];

            if (possible.absolute_fuzzy_equals(center, TOLERANCE*TOLERANCE))
              {
                found_mine = true;
                found_centers[i] = true;
              }
          }
        CPPUNIT_ASSERT(found_mine);
      }

    mesh.comm().max(found_centers);

    for (auto found_it : found_centers)
      CPPUNIT_ASSERT(found_it);
  }

  void testTriangulatorSegments(MeshBase & mesh,
                                TriangulatorInterface & triangulator)
  {
    // The same quad as testTriangulator, but out of order
    mesh.add_point(Point(0,0), 0);
    mesh.add_point(Point(1,2), 1);
    mesh.add_point(Point(1,0), 2);
    mesh.add_point(Point(0,1), 3);

    // Segments to put them in order
    triangulator.segments = {{0,2},{2,1},{1,3},{3,0}};

    this->testTriangulatorBase(mesh, triangulator);
  }

#ifdef LIBMESH_HAVE_TRIANGLE
  void testTriangle()
  {
    Mesh mesh(*TestCommWorld);
    TriangleInterface triangle(mesh);
    testTriangulator(mesh, triangle);
  }

  void testTriangleHoles()
  {
    Mesh mesh(*TestCommWorld);
    TriangleInterface triangle(mesh);
    testTriangulatorHoles(mesh, triangle);
  }

  void testTriangleSegments()
  {
    Mesh mesh(*TestCommWorld);
    TriangleInterface triangle(mesh);
    testTriangulatorSegments(mesh, triangle);
  }
#endif // LIBMESH_HAVE_TRIANGLE

#ifdef LIBMESH_HAVE_POLY2TRI
  void testPoly2Tri()
  {
    Mesh mesh(*TestCommWorld);
    Poly2TriTriangulator p2t_tri(mesh);
    testTriangulator(mesh, p2t_tri);
  }

  void testPoly2TriHoles()
  {
    Mesh mesh(*TestCommWorld);
    Poly2TriTriangulator p2t_tri(mesh);
    testTriangulatorHoles(mesh, p2t_tri);
  }

  void testPoly2TriSegments()
  {
    Mesh mesh(*TestCommWorld);
    Poly2TriTriangulator p2t_tri(mesh);
    testTriangulatorSegments(mesh, p2t_tri);
  }

  void testPoly2TriRefined()
  {
    Mesh mesh(*TestCommWorld);
    mesh.add_point(Point(0,0), 0);
    mesh.add_point(Point(1,0), 1);
    mesh.add_point(Point(1,2), 2);
    mesh.add_point(Point(0,1), 3);

    Poly2TriTriangulator triangulator(mesh);

    // Use the point order to define the boundary, because our
    // Poly2Tri implementation doesn't do convex hulls yet, even when
    // that would give the same answer.
    triangulator.triangulation_type() = TriangulatorInterface::PSLG;

    // Try to insert points!
    triangulator.desired_area() = 0.1;
    triangulator.minimum_angle() = 0;
    triangulator.smooth_after_generating() = false;

    triangulator.triangulate();

    CPPUNIT_ASSERT(mesh.n_elem() > 2);

    Real area = 0;
    for (const auto & elem : mesh.element_ptr_range())
      {
        CPPUNIT_ASSERT_EQUAL(elem->type(), TRI3);

        area += elem->volume();
      }

    LIBMESH_ASSERT_FP_EQUAL(area, 1.5, TOLERANCE*TOLERANCE);
  }
#endif // LIBMESH_HAVE_POLY2TRI

};

CPPUNIT_TEST_SUITE_REGISTRATION( MeshTriangulationTest );
<|endoftext|>


<commit_before>// Important copyright notice:
// Some parts of this file were copied from Mikael Patel's Cosa library, which copyright appears below.
// Some parts of this file directly derive from https://tty1.net/blog/2008/avr-gcc-optimisations_en.html
// Other parts are under Copyright (c) 2016, Jean-Francois Poilpret

/**
 * @file Cosa/Types.h
 * @version 1.0
 *
 * @section License
 * Copyright (C) 2012-2015, Mikael Patel
 *
 * 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.
 *
 * @section Description
 * Common literals, data types and syntax abstractions.
 *
 * This file is part of the Arduino Che Cosa project.
 */
#ifndef UTILITIES_HH
#define	UTILITIES_HH

#include <avr/io.h>
#include <avr/interrupt.h>

#ifndef UNUSED
#define UNUSED __attribute__((unused))
#endif

#ifndef INLINE
#define INLINE __attribute__((always_inline))
#endif

inline uint8_t _lock() INLINE;
inline uint8_t _lock()
{
	uint8_t key = SREG;
	asm volatile("cli" ::: "memory");
	return key;
}

inline void _unlock(uint8_t* key) INLINE;
inline void _unlock(uint8_t* key)
{
  SREG = *key;
  asm volatile("" ::: "memory");
}

#define synchronized \
_Pragma ("GCC diagnostic ignored \"-Wreturn-type\"") \
for (uint8_t __key __attribute__((__cleanup__(_unlock))) = _lock(), i = 1; i != 0; i--)

// Macro found on https://tty1.net/blog/2008/avr-gcc-optimisations_en.html
// This allows processing pointers to SRAM data be performed directly from Y, Z registers
// this may optimize code size on some circumstances
#define FIX_BASE_POINTER(_ptr) __asm__ __volatile__("" : "=b" (_ptr) : "0" (_ptr))

class REGISTER
{
public:
	constexpr REGISTER():ADDR(0) {}
	constexpr REGISTER(const REGISTER& rhs):ADDR(rhs.ADDR) {}
	constexpr REGISTER(uint8_t ADDR):ADDR(ADDR) {}
	uint8_t io_addr() const
	{
		return ADDR - __SFR_OFFSET;
	}
	uint8_t mem_addr() const
	{
		return ADDR;
	}
	operator volatile uint8_t& () const
	{
		return *((volatile uint8_t*) (uint16_t) ADDR);
	}
	operator volatile uint16_t& () const
	{
		return *((volatile uint16_t*) (uint16_t) ADDR);
	}
	//TODO small enhancement: use operators () instead
	void set(uint8_t value) const
	{
		*((volatile uint8_t*) (uint16_t) ADDR) = value;
	}
	uint8_t get() const
	{
		return *((volatile uint8_t*) (uint16_t) ADDR);
	}

private:
	uint8_t ADDR;
};

constexpr uint16_t as_uint16_t(uint8_t high, uint8_t low)
{
	return (high << 8) | low;
}

//TODO Add optimized versions for IOREG registers: set_ioreg_mask, clear_ioreg_mask
inline void set_mask(REGISTER REG, uint8_t MASK) INLINE;
inline void set_mask(REGISTER REG, uint8_t MASK)
{
	((volatile uint8_t&) REG) |= MASK;
}

inline void clear_mask(REGISTER REG, uint8_t MASK) INLINE;
inline void clear_mask(REGISTER REG, uint8_t MASK)
{
	((volatile uint8_t&) REG) &= ~MASK;
}

inline void set_bit_field(REGISTER REG, uint8_t MASK, uint8_t VALUE) INLINE;
inline void set_bit_field(REGISTER REG, uint8_t MASK, uint8_t VALUE)
{
	volatile uint8_t& ref = (volatile uint8_t&) REG;
	ref = (ref & ~MASK) | (VALUE & MASK);
}

inline void set_ioreg_bit(REGISTER IOREG, uint8_t BIT) INLINE;
inline void set_ioreg_bit(REGISTER IOREG, uint8_t BIT)
{
	asm volatile("SBI %[IOREG], %[BIT] \n\t"::[IOREG] "I" (IOREG.io_addr()), [BIT] "I" (BIT));
}

inline void clear_ioreg_bit(REGISTER IOREG, uint8_t BIT) INLINE;
inline void clear_ioreg_bit(REGISTER IOREG, uint8_t BIT)
{
	asm volatile("CBI %[IOREG], %[BIT] \n\t"::[IOREG] "I" (IOREG.io_addr()), [BIT] "I" (BIT));
}

inline bool ioreg_bit_value(REGISTER IOREG, uint8_t BIT) INLINE;
inline bool ioreg_bit_value(REGISTER IOREG, uint8_t BIT)
{
	//TODO check if optimization is possible in actual usage of this function
	// E.g. in code below, extra mov r15,r30 is not absolutely necessary (r30 could be directly used)
	// 2ae:   e0 e0           ldi     r30, 0x00       ; 0
	// 2b0:   4f 99           sbic    0x09, 7 ; 9
	// 2b2:   e1 e0           ldi     r30, 0x01       ; 1
	// 2b4:   fe 2e           mov     r15, r30
	bool result;
	asm volatile(
		"LDI %[RESULT], 0\n\t"			// Clear result value by default
		"SBIC %[IOREG], %[BIT] \n\t"
		"LDI %[RESULT], 1\n\t"			// Bit is set, set result value accordingly
		:[RESULT] "=&d" (result)
		:[IOREG] "I" (IOREG.io_addr()), [BIT] "I" (BIT)
	);
	return result;
}

inline void set_ioreg_byte(REGISTER IOREG, uint8_t value) INLINE;
inline void set_ioreg_byte(REGISTER IOREG, uint8_t value)
{
	asm volatile("OUT %[IOREG], %[VALUE]\n\t"::[IOREG] "I" (IOREG.io_addr()), [VALUE] "r" (value));
}

inline uint8_t get_ioreg_byte(REGISTER IOREG) INLINE;
inline uint8_t get_ioreg_byte(REGISTER IOREG)
{
	uint8_t value = 0;
	asm volatile("IN %[VALUE], %[IOREG]\n\t":[VALUE] "+r" (value):[IOREG] "I" (IOREG.io_addr()));
	return value;
}

// Utilities to handle ISR callbacks
#define HANDLER_HOLDER_(HANDLER) HandlerHolder< HANDLER >

#define CALLBACK_HANDLER_HOLDER_(HANDLER, CALLBACK,...)	\
HandlerHolder< HANDLER >::ArgsHodler< __VA_ARGS__ >::CallbackHolder< CALLBACK >

#define CALL_HANDLER_(HANDLER, CALLBACK,...)	\
CALLBACK_HANDLER_HOLDER_(HANDLER, CALLBACK, ##__VA_ARGS__)::handle

#define REGISTER_ISR_METHOD_(VECTOR, HANDLER, CALLBACK)	\
ISR(VECTOR)												\
{														\
	CALL_HANDLER_(HANDLER , CALLBACK)();				\
}

#define REGISTER_ISR_FUNCTION_(VECTOR, CALLBACK)	\
ISR(VECTOR)											\
{													\
	CALLBACK ();									\
}

template<typename Handler> void register_handler(Handler&);
template<typename Handler>
class HandlerHolder
{
public:
	static Handler* handler()
	{
		return _handler;
	}

	using Holder = HandlerHolder<Handler>;

	template<typename... Args>
	struct ArgsHodler
	{
		template<void (Handler::*Callback)(Args...)>
		struct CallbackHolder
		{
			static void handle(Args... args)
			{
				Handler* handler_instance = Holder::handler();
				FIX_BASE_POINTER(handler_instance);
				(handler_instance->*Callback)(args...);
			}
		};
	};

private:
	static Handler* _handler;
	friend void register_handler<Handler>(Handler&);
};

template<typename Handler>
Handler* HandlerHolder<Handler>::_handler = 0;

template<typename Handler>
void register_handler(Handler& handler)
{
	HandlerHolder<Handler>::_handler = &handler;
}

// Useful macro to iterate
// NOTE: these macros have been inspired by several readings:
// http://stackoverflow.com/questions/11761703/overloading-macro-on-number-of-arguments
// http://stackoverflow.com/questions/1872220/is-it-possible-to-iterate-over-arguments-in-variadic-macros
// https://github.com/pfultz2/Cloak/wiki/C-Preprocessor-tricks,-tips,-and-idioms

#define EMPTY(...)
#define CAT(X, Y) X ## Y
#define CAT3(X, Y, Z) X ## Y ## Z
#define COMMA() ,
#define STRINGIFY(X, ...) #X
#define ID(X, ...) X

#define FE_0_(M, DATA, FIRST, SEP, LAST)
#define FE_1_(M, DATA, FIRST, SEP, LAST, X1) FIRST() M(X1, DATA) LAST()
#define FE_2_(M, DATA, FIRST, SEP, LAST, X1, X2) FIRST() M(X1, DATA) SEP() M(X2, DATA) LAST()
#define FE_3_(M, DATA, FIRST, SEP, LAST, X1, X2, X3) FIRST() M(X1, DATA) SEP() M(X2, DATA) SEP() M(X3, DATA) LAST()
#define FE_4_(M, DATA, FIRST, SEP, LAST, X1, X2, X3, X4) FIRST()  M(X1, DATA) SEP() M(X2, DATA) SEP() M(X3, DATA) SEP() M(X4, DATA) LAST()
#define FE_5_(M, DATA, FIRST, SEP, LAST, X1, X2, X3, X4, X5) FIRST() M(X1, DATA) SEP() M(X2, DATA) SEP() M(X3, DATA) SEP() M(X4, DATA) SEP() M(X5, DATA) LAST()
#define FE_6_(M, DATA, FIRST, SEP, LAST, X1, X2, X3, X4, X5, X6) FIRST() M(X1, DATA) SEP() M(X2, DATA) SEP() M(X3, DATA) SEP() M(X4, DATA) SEP() M(X5, DATA) SEP() M(X6, DATA) LAST()
#define FE_7_(M, DATA, FIRST, SEP, LAST, X1, X2, X3, X4, X5, X6, X7) FIRST() M(X1, DATA) SEP() M(X2, DATA) SEP() M(X3, DATA) SEP() M(X4, DATA) SEP() M(X5, DATA) SEP() M(X6, DATA) SEP() M(X7, DATA) LAST()
#define FE_8_(M, DATA, FIRST, SEP, LAST, X1, X2, X3, X4, X5, X6, X7, X8) FIRST() M(X1, DATA) SEP() M(X2, DATA) SEP() M(X3, DATA) SEP() M(X4, DATA) SEP() M(X5, DATA) SEP() M(X6, DATA) SEP() M(X7, DATA) SEP() M(X8, DATA) LAST()
#define FE_9_(M, DATA, FIRST, SEP, LAST, X1, X2, X3, X4, X5, X6, X7, X8, X9) FIRST() M(X1, DATA) SEP() M(X2, DATA) SEP() M(X3, DATA) SEP() M(X4, DATA) SEP() M(X5, DATA) SEP() M(X6, DATA) SEP() M(X7, DATA) SEP() M(X8, DATA) SEP() M(X9, DATA) LAST()

#define GET_MACRO_9_(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, NAME,...) NAME

// FOR_EACH executes M macro for each argument passed to it, returns empty if passed an empty list.
// Number of arguments in variadic list must be between 0 and 9
#define FOR_EACH(M, DATA, ...) GET_MACRO_9_(unused, ##__VA_ARGS__, FE_9_, FE_8_, FE_7_, FE_6_, FE_5_, FE_4_, FE_3_, FE_2_, FE_1_, FE_0_)(M, DATA, EMPTY, EMPTY, EMPTY, ## __VA_ARGS__)
// FOR_EACH_SEP executes M macro for each argument passed to it, separates each transformed value with SEP(),
// prepends FIRST() and appends LAST() if result list is not empty; returns empty if passed an empty list
// Number of arguments in variadic list must be between 0 and 9
#define FOR_EACH_SEP(M, DATA, FIRST, SEP, LAST, ...) GET_MACRO_9_(unused, ##__VA_ARGS__, FE_9_, FE_8_, FE_7_, FE_6_, FE_5_, FE_4_, FE_3_, FE_2_, FE_1_, FE_0_)(M, DATA, FIRST, SEP, LAST, ##__VA_ARGS__)

#endif	/* UTILITIES_HH */
<commit_msg>Replace Cosa's synchronized implementation with direct use of AVR atomic macros.<commit_after>// Important copyright notice:
// Some parts of this file directly derive from https://tty1.net/blog/2008/avr-gcc-optimisations_en.html
// Other parts are under Copyright (c) 2016, Jean-Francois Poilpret

#ifndef UTILITIES_HH
#define	UTILITIES_HH

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/atomic.h>

#ifndef UNUSED
#define UNUSED __attribute__((unused))
#endif

#ifndef INLINE
#define INLINE __attribute__((always_inline))
#endif

#define synchronized \
_Pragma ("GCC diagnostic ignored \"-Wreturn-type\"") \
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)

// Macro found on https://tty1.net/blog/2008/avr-gcc-optimisations_en.html
// This allows processing pointers to SRAM data be performed directly from Y, Z registers
// this may optimize code size on some circumstances
#define FIX_BASE_POINTER(_ptr) __asm__ __volatile__("" : "=b" (_ptr) : "0" (_ptr))

class REGISTER
{
public:
	constexpr REGISTER():ADDR(0) {}
	constexpr REGISTER(const REGISTER& rhs):ADDR(rhs.ADDR) {}
	constexpr REGISTER(uint8_t ADDR):ADDR(ADDR) {}
	uint8_t io_addr() const
	{
		return ADDR - __SFR_OFFSET;
	}
	uint8_t mem_addr() const
	{
		return ADDR;
	}
	operator volatile uint8_t& () const
	{
		return *((volatile uint8_t*) (uint16_t) ADDR);
	}
	operator volatile uint16_t& () const
	{
		return *((volatile uint16_t*) (uint16_t) ADDR);
	}
	//TODO small enhancement: use operators () instead
	void set(uint8_t value) const
	{
		*((volatile uint8_t*) (uint16_t) ADDR) = value;
	}
	uint8_t get() const
	{
		return *((volatile uint8_t*) (uint16_t) ADDR);
	}

private:
	uint8_t ADDR;
};

constexpr uint16_t as_uint16_t(uint8_t high, uint8_t low)
{
	return (high << 8) | low;
}

//TODO Add optimized versions for IOREG registers: set_ioreg_mask, clear_ioreg_mask
inline void set_mask(REGISTER REG, uint8_t MASK) INLINE;
inline void set_mask(REGISTER REG, uint8_t MASK)
{
	((volatile uint8_t&) REG) |= MASK;
}

inline void clear_mask(REGISTER REG, uint8_t MASK) INLINE;
inline void clear_mask(REGISTER REG, uint8_t MASK)
{
	((volatile uint8_t&) REG) &= ~MASK;
}

inline void set_bit_field(REGISTER REG, uint8_t MASK, uint8_t VALUE) INLINE;
inline void set_bit_field(REGISTER REG, uint8_t MASK, uint8_t VALUE)
{
	volatile uint8_t& ref = (volatile uint8_t&) REG;
	ref = (ref & ~MASK) | (VALUE & MASK);
}

inline void set_ioreg_bit(REGISTER IOREG, uint8_t BIT) INLINE;
inline void set_ioreg_bit(REGISTER IOREG, uint8_t BIT)
{
	asm volatile("SBI %[IOREG], %[BIT] \n\t"::[IOREG] "I" (IOREG.io_addr()), [BIT] "I" (BIT));
}

inline void clear_ioreg_bit(REGISTER IOREG, uint8_t BIT) INLINE;
inline void clear_ioreg_bit(REGISTER IOREG, uint8_t BIT)
{
	asm volatile("CBI %[IOREG], %[BIT] \n\t"::[IOREG] "I" (IOREG.io_addr()), [BIT] "I" (BIT));
}

inline bool ioreg_bit_value(REGISTER IOREG, uint8_t BIT) INLINE;
inline bool ioreg_bit_value(REGISTER IOREG, uint8_t BIT)
{
	//TODO check if optimization is possible in actual usage of this function
	// E.g. in code below, extra mov r15,r30 is not absolutely necessary (r30 could be directly used)
	// 2ae:   e0 e0           ldi     r30, 0x00       ; 0
	// 2b0:   4f 99           sbic    0x09, 7 ; 9
	// 2b2:   e1 e0           ldi     r30, 0x01       ; 1
	// 2b4:   fe 2e           mov     r15, r30
	bool result;
	asm volatile(
		"LDI %[RESULT], 0\n\t"			// Clear result value by default
		"SBIC %[IOREG], %[BIT] \n\t"
		"LDI %[RESULT], 1\n\t"			// Bit is set, set result value accordingly
		:[RESULT] "=&d" (result)
		:[IOREG] "I" (IOREG.io_addr()), [BIT] "I" (BIT)
	);
	return result;
}

inline void set_ioreg_byte(REGISTER IOREG, uint8_t value) INLINE;
inline void set_ioreg_byte(REGISTER IOREG, uint8_t value)
{
	asm volatile("OUT %[IOREG], %[VALUE]\n\t"::[IOREG] "I" (IOREG.io_addr()), [VALUE] "r" (value));
}

inline uint8_t get_ioreg_byte(REGISTER IOREG) INLINE;
inline uint8_t get_ioreg_byte(REGISTER IOREG)
{
	uint8_t value = 0;
	asm volatile("IN %[VALUE], %[IOREG]\n\t":[VALUE] "+r" (value):[IOREG] "I" (IOREG.io_addr()));
	return value;
}

// Utilities to handle ISR callbacks
#define HANDLER_HOLDER_(HANDLER) HandlerHolder< HANDLER >

#define CALLBACK_HANDLER_HOLDER_(HANDLER, CALLBACK,...)	\
HandlerHolder< HANDLER >::ArgsHodler< __VA_ARGS__ >::CallbackHolder< CALLBACK >

#define CALL_HANDLER_(HANDLER, CALLBACK,...)	\
CALLBACK_HANDLER_HOLDER_(HANDLER, CALLBACK, ##__VA_ARGS__)::handle

#define REGISTER_ISR_METHOD_(VECTOR, HANDLER, CALLBACK)	\
ISR(VECTOR)												\
{														\
	CALL_HANDLER_(HANDLER , CALLBACK)();				\
}

#define REGISTER_ISR_FUNCTION_(VECTOR, CALLBACK)	\
ISR(VECTOR)											\
{													\
	CALLBACK ();									\
}

template<typename Handler> void register_handler(Handler&);
template<typename Handler>
class HandlerHolder
{
public:
	static Handler* handler()
	{
		return _handler;
	}

	using Holder = HandlerHolder<Handler>;

	template<typename... Args>
	struct ArgsHodler
	{
		template<void (Handler::*Callback)(Args...)>
		struct CallbackHolder
		{
			static void handle(Args... args)
			{
				Handler* handler_instance = Holder::handler();
				FIX_BASE_POINTER(handler_instance);
				(handler_instance->*Callback)(args...);
			}
		};
	};

private:
	static Handler* _handler;
	friend void register_handler<Handler>(Handler&);
};

template<typename Handler>
Handler* HandlerHolder<Handler>::_handler = 0;

template<typename Handler>
void register_handler(Handler& handler)
{
	HandlerHolder<Handler>::_handler = &handler;
}

// Useful macro to iterate
// NOTE: these macros have been inspired by several readings:
// http://stackoverflow.com/questions/11761703/overloading-macro-on-number-of-arguments
// http://stackoverflow.com/questions/1872220/is-it-possible-to-iterate-over-arguments-in-variadic-macros
// https://github.com/pfultz2/Cloak/wiki/C-Preprocessor-tricks,-tips,-and-idioms

#define EMPTY(...)
#define CAT(X, Y) X ## Y
#define CAT3(X, Y, Z) X ## Y ## Z
#define COMMA() ,
#define STRINGIFY(X, ...) #X
#define ID(X, ...) X

#define FE_0_(M, DATA, FIRST, SEP, LAST)
#define FE_1_(M, DATA, FIRST, SEP, LAST, X1) FIRST() M(X1, DATA) LAST()
#define FE_2_(M, DATA, FIRST, SEP, LAST, X1, X2) FIRST() M(X1, DATA) SEP() M(X2, DATA) LAST()
#define FE_3_(M, DATA, FIRST, SEP, LAST, X1, X2, X3) FIRST() M(X1, DATA) SEP() M(X2, DATA) SEP() M(X3, DATA) LAST()
#define FE_4_(M, DATA, FIRST, SEP, LAST, X1, X2, X3, X4) FIRST()  M(X1, DATA) SEP() M(X2, DATA) SEP() M(X3, DATA) SEP() M(X4, DATA) LAST()
#define FE_5_(M, DATA, FIRST, SEP, LAST, X1, X2, X3, X4, X5) FIRST() M(X1, DATA) SEP() M(X2, DATA) SEP() M(X3, DATA) SEP() M(X4, DATA) SEP() M(X5, DATA) LAST()
#define FE_6_(M, DATA, FIRST, SEP, LAST, X1, X2, X3, X4, X5, X6) FIRST() M(X1, DATA) SEP() M(X2, DATA) SEP() M(X3, DATA) SEP() M(X4, DATA) SEP() M(X5, DATA) SEP() M(X6, DATA) LAST()
#define FE_7_(M, DATA, FIRST, SEP, LAST, X1, X2, X3, X4, X5, X6, X7) FIRST() M(X1, DATA) SEP() M(X2, DATA) SEP() M(X3, DATA) SEP() M(X4, DATA) SEP() M(X5, DATA) SEP() M(X6, DATA) SEP() M(X7, DATA) LAST()
#define FE_8_(M, DATA, FIRST, SEP, LAST, X1, X2, X3, X4, X5, X6, X7, X8) FIRST() M(X1, DATA) SEP() M(X2, DATA) SEP() M(X3, DATA) SEP() M(X4, DATA) SEP() M(X5, DATA) SEP() M(X6, DATA) SEP() M(X7, DATA) SEP() M(X8, DATA) LAST()
#define FE_9_(M, DATA, FIRST, SEP, LAST, X1, X2, X3, X4, X5, X6, X7, X8, X9) FIRST() M(X1, DATA) SEP() M(X2, DATA) SEP() M(X3, DATA) SEP() M(X4, DATA) SEP() M(X5, DATA) SEP() M(X6, DATA) SEP() M(X7, DATA) SEP() M(X8, DATA) SEP() M(X9, DATA) LAST()

#define GET_MACRO_9_(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, NAME,...) NAME

// FOR_EACH executes M macro for each argument passed to it, returns empty if passed an empty list.
// Number of arguments in variadic list must be between 0 and 9
#define FOR_EACH(M, DATA, ...) GET_MACRO_9_(unused, ##__VA_ARGS__, FE_9_, FE_8_, FE_7_, FE_6_, FE_5_, FE_4_, FE_3_, FE_2_, FE_1_, FE_0_)(M, DATA, EMPTY, EMPTY, EMPTY, ## __VA_ARGS__)
// FOR_EACH_SEP executes M macro for each argument passed to it, separates each transformed value with SEP(),
// prepends FIRST() and appends LAST() if result list is not empty; returns empty if passed an empty list
// Number of arguments in variadic list must be between 0 and 9
#define FOR_EACH_SEP(M, DATA, FIRST, SEP, LAST, ...) GET_MACRO_9_(unused, ##__VA_ARGS__, FE_9_, FE_8_, FE_7_, FE_6_, FE_5_, FE_4_, FE_3_, FE_2_, FE_1_, FE_0_)(M, DATA, FIRST, SEP, LAST, ##__VA_ARGS__)

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