<commit_before>/***************************************************************************//**
 * context.cpp
 *
 * The main root structure of cmgui.
 */
/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (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.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is cmgui.
 *
 * The Initial Developer of the Original Code is
 * Auckland Uniservices Ltd, Auckland, New Zealand.
 * Portions created by the Initial Developer are Copyright (C) 2005
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

#include "zinc/fieldgroup.h"
#include "zinc/graphicsmodule.h"
#include "context/context.h"
#include "curve/curve.h"
#include "general/debug.h"
#include "general/mystring.h"
#include "general/object.h"
#include "graphics/scene_viewer.h"
#include "graphics/graphics_module.h"
#include "graphics/rendition.h"
#include "selection/any_object_selection.h"
#include "region/cmiss_region.h"
#include "time/time_keeper.h"
//-- #include "user_interface/event_dispatcher.h"
/* following is temporary until circular references are removed for Cmiss_region  */
#include "region/cmiss_region_private.h"

struct Context *Cmiss_context_create(const char *id)
{
	struct Context *context = NULL;
	if (ALLOCATE(context, struct Context, 1))
	{
		context->graphics_module = NULL;
		context->root_region = NULL;
		context->id = duplicate_string(id);
		context->any_object_selection = NULL;
		context->element_point_ranges_selection = NULL;
		context->scene_viewer_package = NULL;
		context->io_stream_package = NULL;
		context->curve_manager = NULL;
		context->time_keeper = 0;
		context->access_count = 1;
	}

	return context;
}

int Cmiss_context_destroy(struct Context **context_address)
{
	int return_code = 0;
	struct Context *context = NULL;

	if (context_address && NULL != (context = *context_address))
	{
		context->access_count--;
		if (0 == context->access_count)
		{
			if (context->id)
				DEALLOCATE(context->id);
			if (context->graphics_module)
				Cmiss_graphics_module_destroy(&context->graphics_module);
			if (context->root_region)
			{
				/* need the following due to circular references where field owned by region references region itself;
				 * when following removed also remove #include "region/cmiss_region_private.h". Also rendition
				 * has a computed_field manager callback so it must be deleted before detaching fields hierarchical */
				Cmiss_region_detach_fields_hierarchical(context->root_region);
				DEACCESS(Cmiss_region)(&context->root_region);
			}
			if (context->scene_viewer_package)
			{
				Cmiss_scene_viewer_package_destroy(&context->scene_viewer_package);
			}
			if (context->any_object_selection)
			{
				DESTROY(Any_object_selection)(&context->any_object_selection);
			}
			if (context->element_point_ranges_selection)
			{
				DESTROY(Element_point_ranges_selection)(&context->element_point_ranges_selection);
			}
			if (context->curve_manager)
			{
				DESTROY(MANAGER(Curve))(&context->curve_manager);
			}
			if (context->io_stream_package)
			{
				DESTROY(IO_stream_package)(&context->io_stream_package);
			}
			if (context->time_keeper)
			{
				Cmiss_time_keeper_destroy(&context->time_keeper);
			}
			DEALLOCATE(*context_address);
		}
		*context_address = NULL;
		return_code = 1;
	}
	else
	{
		display_message(ERROR_MESSAGE,
			"Cmiss_context_destroy.  Missing context address");
		return_code = 0;
	}

	/* Write out any memory blocks still ALLOCATED when MEMORY_CHECKING is
		on.  When MEMORY_CHECKING is off this function does nothing */
	list_memory(/*count_number*/0, /*show_pointers*/0, /*increment_counter*/0,
		/*show_structures*/1);

	return return_code;
}

struct Context *Cmiss_context_access(struct Context *context)
{
	if (context)
	{
		context->access_count++;
	}
	else
	{
		display_message(ERROR_MESSAGE,
			"Cmiss_context_access.  Missing context");
	}
	return context;
}

struct Cmiss_region *Cmiss_context_get_default_region(struct Context *context)
{
	struct Cmiss_region *root_region = 0;

	if (context)
	{
		if (!context->root_region)
		{
			context->root_region = Cmiss_region_create_internal();
		}
		root_region = ACCESS(Cmiss_region)(context->root_region);
	}
	else
	{
		display_message(ERROR_MESSAGE,
			"Cmiss_context_get_default_region.  Missing context");
	}

	return root_region;
}

struct Cmiss_graphics_module *Cmiss_context_get_default_graphics_module(struct Context *context)
{
	struct Cmiss_graphics_module *graphics_module = 0;

	if (context)
	{
		if (!context->graphics_module)
		{
			context->graphics_module = Cmiss_graphics_module_create(context);
		}
		graphics_module = Cmiss_graphics_module_access(context->graphics_module);
	}
	else
	{
		display_message(ERROR_MESSAGE,
			"Cmiss_context_get_default_graphics_module.  Missing context");
	}

	return graphics_module;
}

struct Cmiss_region *Cmiss_context_create_region(struct Context *context)
{
	Cmiss_region *region = NULL;

	if (context)
	{
		// all regions share the element shapes and bases from the default_region
		if (!context->root_region)
		{
			Cmiss_region *default_region = Cmiss_context_get_default_region(context);
			Cmiss_region_destroy(&default_region);
		}
		region = Cmiss_region_create_region(context->root_region);
	}
	else
	{
		display_message(ERROR_MESSAGE,
			"Cmiss_context_create_region.  Missing context");
	}
	return region;
}

struct Any_object_selection *Cmiss_context_get_any_object_selection(
	struct Context *context)
{
	struct Any_object_selection *any_object_selection = NULL;
	if (context)
	{
		if (!context->any_object_selection)
		{
			context->any_object_selection = CREATE(Any_object_selection)();
		}
		any_object_selection = context->any_object_selection;
	}
	else
	{
		display_message(ERROR_MESSAGE,
			"Cmiss_context_get_any_object_selection.  Missing context.");
	}
	return any_object_selection;
}

struct Element_point_ranges_selection *Cmiss_context_get_element_point_ranges_selection(
	struct Context *context)
{
	struct Element_point_ranges_selection *element_point_ranges_selection = NULL;
	if (context)
	{
		if (!context->element_point_ranges_selection)
		{
			context->element_point_ranges_selection = CREATE(Element_point_ranges_selection)();
		}
		element_point_ranges_selection = context->element_point_ranges_selection;
	}
	else
	{
		display_message(ERROR_MESSAGE,
			"Cmiss_context_get_element_point_ranges_selection.  Missing context.");
	}
	return element_point_ranges_selection;
}

struct IO_stream_package *Cmiss_context_get_default_IO_stream_package(
	struct Context *context)
{
	struct IO_stream_package *io_stream_package = NULL;
	if (context)
	{
		if (!context->io_stream_package)
		{
			context->io_stream_package = CREATE(IO_stream_package)();
		}
		io_stream_package = context->io_stream_package;
	}
	else
	{
		display_message(ERROR_MESSAGE,
			"Cmiss_context_get_default_IO_stream_package.  Missing context.");
	}

	return io_stream_package;
}

Cmiss_time_keeper_id Cmiss_context_get_default_time_keeper(Cmiss_context_id context)
{
	Cmiss_time_keeper_id time_keeper = 0;
	if (context)
	{
		if (!context->time_keeper)
		{
			context->time_keeper = CREATE(Time_keeper)("default", 0);
		}
		time_keeper = context->time_keeper;
	}
	else
	{
		display_message(ERROR_MESSAGE,
			"Cmiss_context_get_default_time_keeper.  Missing context.");
	}

	return time_keeper;
}

Cmiss_scene_viewer_package_id Cmiss_context_get_default_scene_viewer_package(
	Cmiss_context_id context)
{
	Cmiss_scene_viewer_package *scene_viewer_package = NULL;
	if (context)
	{
		if (!context->scene_viewer_package)
		{
			Cmiss_graphics_module_id graphics_module = Cmiss_context_get_default_graphics_module(context);
			if (graphics_module)
			{
				struct Light *default_light =
					Cmiss_graphics_module_get_default_light(graphics_module);
				struct Light_model *default_light_model =
					Cmiss_graphics_module_get_default_light_model(graphics_module);
				struct Scene *default_scene =
					Cmiss_graphics_module_get_default_scene(graphics_module);
				Colour default_background_colour;
				default_background_colour.red = 0.0;
				default_background_colour.green = 0.0;
				default_background_colour.blue = 0.0;
				context->scene_viewer_package = CREATE(Cmiss_scene_viewer_package)
					(&default_background_colour,
						/* interactive_tool_manager */0,
						Cmiss_graphics_module_get_light_manager(graphics_module), default_light,
						Cmiss_graphics_module_get_light_model_manager(graphics_module), default_light_model,
						Cmiss_graphics_module_get_scene_manager(graphics_module), default_scene);
				DEACCESS(Light_model)(&default_light_model);
				DEACCESS(Light)(&default_light);
				Cmiss_scene_destroy(&default_scene);
			}
		}
		scene_viewer_package = Cmiss_scene_viewer_package_access(context->scene_viewer_package);
	}
	else
	{
		display_message(ERROR_MESSAGE,
			"Cmiss_context_get_default_scene_viewer_package.  "
			"Missing context");
	}
	return scene_viewer_package;
}

struct MANAGER(Curve) *Cmiss_context_get_default_curve_manager(
	Cmiss_context_id context)
{
	MANAGER(Curve) *curve_manager = NULL;
	if (context)
	{
		if (!context->curve_manager)
		{
			context->curve_manager = CREATE(MANAGER(Curve))();
		}
		curve_manager = context->curve_manager;
	}
	else
	{
		display_message(ERROR_MESSAGE,
			"Cmiss_context_get_default_curve_manager.  "
			"Missing context");
	}
	return curve_manager;
}

<commit_msg>Adding access to returned time_keeper in Cmiss_context_get_default_time_keeper.  https://tracker.physiomeproject.org/show_bug.cgi?id=3528<commit_after>/***************************************************************************//**
 * context.cpp
 *
 * The main root structure of cmgui.
 */
/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (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.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is cmgui.
 *
 * The Initial Developer of the Original Code is
 * Auckland Uniservices Ltd, Auckland, New Zealand.
 * Portions created by the Initial Developer are Copyright (C) 2005
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

#include "zinc/fieldgroup.h"
#include "zinc/graphicsmodule.h"
#include "context/context.h"
#include "curve/curve.h"
#include "general/debug.h"
#include "general/mystring.h"
#include "general/object.h"
#include "graphics/scene_viewer.h"
#include "graphics/graphics_module.h"
#include "graphics/rendition.h"
#include "selection/any_object_selection.h"
#include "region/cmiss_region.h"
#include "time/time_keeper.h"
//-- #include "user_interface/event_dispatcher.h"
/* following is temporary until circular references are removed for Cmiss_region  */
#include "region/cmiss_region_private.h"

struct Context *Cmiss_context_create(const char *id)
{
	struct Context *context = NULL;
	if (ALLOCATE(context, struct Context, 1))
	{
		context->graphics_module = NULL;
		context->root_region = NULL;
		context->id = duplicate_string(id);
		context->any_object_selection = NULL;
		context->element_point_ranges_selection = NULL;
		context->scene_viewer_package = NULL;
		context->io_stream_package = NULL;
		context->curve_manager = NULL;
		context->time_keeper = 0;
		context->access_count = 1;
	}

	return context;
}

int Cmiss_context_destroy(struct Context **context_address)
{
	int return_code = 0;
	struct Context *context = NULL;

	if (context_address && NULL != (context = *context_address))
	{
		context->access_count--;
		if (0 == context->access_count)
		{
			if (context->id)
				DEALLOCATE(context->id);
			if (context->graphics_module)
				Cmiss_graphics_module_destroy(&context->graphics_module);
			if (context->root_region)
			{
				/* need the following due to circular references where field owned by region references region itself;
				 * when following removed also remove #include "region/cmiss_region_private.h". Also rendition
				 * has a computed_field manager callback so it must be deleted before detaching fields hierarchical */
				Cmiss_region_detach_fields_hierarchical(context->root_region);
				DEACCESS(Cmiss_region)(&context->root_region);
			}
			if (context->scene_viewer_package)
			{
				Cmiss_scene_viewer_package_destroy(&context->scene_viewer_package);
			}
			if (context->any_object_selection)
			{
				DESTROY(Any_object_selection)(&context->any_object_selection);
			}
			if (context->element_point_ranges_selection)
			{
				DESTROY(Element_point_ranges_selection)(&context->element_point_ranges_selection);
			}
			if (context->curve_manager)
			{
				DESTROY(MANAGER(Curve))(&context->curve_manager);
			}
			if (context->io_stream_package)
			{
				DESTROY(IO_stream_package)(&context->io_stream_package);
			}
			if (context->time_keeper)
			{
				Cmiss_time_keeper_destroy(&context->time_keeper);
			}
			DEALLOCATE(*context_address);
		}
		*context_address = NULL;
		return_code = 1;
	}
	else
	{
		display_message(ERROR_MESSAGE,
			"Cmiss_context_destroy.  Missing context address");
		return_code = 0;
	}

	/* Write out any memory blocks still ALLOCATED when MEMORY_CHECKING is
		on.  When MEMORY_CHECKING is off this function does nothing */
	list_memory(/*count_number*/0, /*show_pointers*/0, /*increment_counter*/0,
		/*show_structures*/1);

	return return_code;
}

struct Context *Cmiss_context_access(struct Context *context)
{
	if (context)
	{
		context->access_count++;
	}
	else
	{
		display_message(ERROR_MESSAGE,
			"Cmiss_context_access.  Missing context");
	}
	return context;
}

struct Cmiss_region *Cmiss_context_get_default_region(struct Context *context)
{
	struct Cmiss_region *root_region = 0;

	if (context)
	{
		if (!context->root_region)
		{
			context->root_region = Cmiss_region_create_internal();
		}
		root_region = ACCESS(Cmiss_region)(context->root_region);
	}
	else
	{
		display_message(ERROR_MESSAGE,
			"Cmiss_context_get_default_region.  Missing context");
	}

	return root_region;
}

struct Cmiss_graphics_module *Cmiss_context_get_default_graphics_module(struct Context *context)
{
	struct Cmiss_graphics_module *graphics_module = 0;

	if (context)
	{
		if (!context->graphics_module)
		{
			context->graphics_module = Cmiss_graphics_module_create(context);
		}
		graphics_module = Cmiss_graphics_module_access(context->graphics_module);
	}
	else
	{
		display_message(ERROR_MESSAGE,
			"Cmiss_context_get_default_graphics_module.  Missing context");
	}

	return graphics_module;
}

struct Cmiss_region *Cmiss_context_create_region(struct Context *context)
{
	Cmiss_region *region = NULL;

	if (context)
	{
		// all regions share the element shapes and bases from the default_region
		if (!context->root_region)
		{
			Cmiss_region *default_region = Cmiss_context_get_default_region(context);
			Cmiss_region_destroy(&default_region);
		}
		region = Cmiss_region_create_region(context->root_region);
	}
	else
	{
		display_message(ERROR_MESSAGE,
			"Cmiss_context_create_region.  Missing context");
	}
	return region;
}

struct Any_object_selection *Cmiss_context_get_any_object_selection(
	struct Context *context)
{
	struct Any_object_selection *any_object_selection = NULL;
	if (context)
	{
		if (!context->any_object_selection)
		{
			context->any_object_selection = CREATE(Any_object_selection)();
		}
		any_object_selection = context->any_object_selection;
	}
	else
	{
		display_message(ERROR_MESSAGE,
			"Cmiss_context_get_any_object_selection.  Missing context.");
	}
	return any_object_selection;
}

struct Element_point_ranges_selection *Cmiss_context_get_element_point_ranges_selection(
	struct Context *context)
{
	struct Element_point_ranges_selection *element_point_ranges_selection = NULL;
	if (context)
	{
		if (!context->element_point_ranges_selection)
		{
			context->element_point_ranges_selection = CREATE(Element_point_ranges_selection)();
		}
		element_point_ranges_selection = context->element_point_ranges_selection;
	}
	else
	{
		display_message(ERROR_MESSAGE,
			"Cmiss_context_get_element_point_ranges_selection.  Missing context.");
	}
	return element_point_ranges_selection;
}

struct IO_stream_package *Cmiss_context_get_default_IO_stream_package(
	struct Context *context)
{
	struct IO_stream_package *io_stream_package = NULL;
	if (context)
	{
		if (!context->io_stream_package)
		{
			context->io_stream_package = CREATE(IO_stream_package)();
		}
		io_stream_package = context->io_stream_package;
	}
	else
	{
		display_message(ERROR_MESSAGE,
			"Cmiss_context_get_default_IO_stream_package.  Missing context.");
	}

	return io_stream_package;
}

Cmiss_time_keeper_id Cmiss_context_get_default_time_keeper(Cmiss_context_id context)
{
	Cmiss_time_keeper_id time_keeper = 0;
	if (context)
	{
		if (!context->time_keeper)
		{
			context->time_keeper = CREATE(Time_keeper)("default", 0);
		}
		time_keeper = Cmiss_time_keeper_access(context->time_keeper);
	}
	else
	{
		display_message(ERROR_MESSAGE,
			"Cmiss_context_get_default_time_keeper.  Missing context.");
	}

	return time_keeper;
}

Cmiss_scene_viewer_package_id Cmiss_context_get_default_scene_viewer_package(
	Cmiss_context_id context)
{
	Cmiss_scene_viewer_package *scene_viewer_package = NULL;
	if (context)
	{
		if (!context->scene_viewer_package)
		{
			Cmiss_graphics_module_id graphics_module = Cmiss_context_get_default_graphics_module(context);
			if (graphics_module)
			{
				struct Light *default_light =
					Cmiss_graphics_module_get_default_light(graphics_module);
				struct Light_model *default_light_model =
					Cmiss_graphics_module_get_default_light_model(graphics_module);
				struct Scene *default_scene =
					Cmiss_graphics_module_get_default_scene(graphics_module);
				Colour default_background_colour;
				default_background_colour.red = 0.0;
				default_background_colour.green = 0.0;
				default_background_colour.blue = 0.0;
				context->scene_viewer_package = CREATE(Cmiss_scene_viewer_package)
					(&default_background_colour,
						/* interactive_tool_manager */0,
						Cmiss_graphics_module_get_light_manager(graphics_module), default_light,
						Cmiss_graphics_module_get_light_model_manager(graphics_module), default_light_model,
						Cmiss_graphics_module_get_scene_manager(graphics_module), default_scene);
				DEACCESS(Light_model)(&default_light_model);
				DEACCESS(Light)(&default_light);
				Cmiss_scene_destroy(&default_scene);
			}
		}
		scene_viewer_package = Cmiss_scene_viewer_package_access(context->scene_viewer_package);
	}
	else
	{
		display_message(ERROR_MESSAGE,
			"Cmiss_context_get_default_scene_viewer_package.  "
			"Missing context");
	}
	return scene_viewer_package;
}

struct MANAGER(Curve) *Cmiss_context_get_default_curve_manager(
	Cmiss_context_id context)
{
	MANAGER(Curve) *curve_manager = NULL;
	if (context)
	{
		if (!context->curve_manager)
		{
			context->curve_manager = CREATE(MANAGER(Curve))();
		}
		curve_manager = context->curve_manager;
	}
	else
	{
		display_message(ERROR_MESSAGE,
			"Cmiss_context_get_default_curve_manager.  "
			"Missing context");
	}
	return curve_manager;
}

<|endoftext|>


<commit_before>// @(#)root/thread:$Id$
// Authors: Enric Tejedor CERN  12/09/2016
//          Philippe Canal FNAL 12/09/2016

/*************************************************************************
 * Copyright (C) 1995-2016, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

/** \class TRWSpinLock
    \brief An implementation of a read-write lock with an internal spin lock.

This class provides an implementation of a read-write lock that uses an
internal spin lock and a condition variable to synchronize readers and
writers when necessary.

The implementation tries to make faster the scenario when readers come
and go but there is no writer. In that case, readers will not pay the
price of taking the internal spin lock.

Moreover, this RW lock tries to be fair with writers, giving them the
possibility to claim the lock and wait for only the remaining readers,
thus preventing starvation.
*/

#include "TRWSpinLock.h"

using namespace ROOT;

////////////////////////////////////////////////////////////////////////////
/// Acquire the lock in read mode.
void TRWSpinLock::ReadLock()
{
  ++fReaderReservation;
   if (!fWriter) {
      // There is no writer, go freely to the critical section
      ++fReaders;
      --fReaderReservation;
   } else {
      // A writer claimed the RW lock, we will need to wait on the
      // internal lock
      --fReaderReservation;

      std::unique_lock<ROOT::TSpinMutex> lock(fMutex);

      // Wait for writers, if any
      fCond.wait(lock, [this]{ return !fWriter; });

      // This RW lock now belongs to the readers
      ++fReaders;

      lock.unlock();
   }
}

//////////////////////////////////////////////////////////////////////////
/// Release the lock in read mode.
void TRWSpinLock::ReadUnLock()
{
   --fReaders;
   if (fWriterReservation && fReaders == 0) {
      // We still need to lock here to prevent interleaving with a writer
      std::lock_guard<ROOT::TSpinMutex> lock(fMutex);

      // Make sure you wake up a writer, if any
      // Note: spurrious wakeups are okay, fReaders
      // will be checked again in WriteLock
      fCond.notify_all();
   }
}

//////////////////////////////////////////////////////////////////////////
/// Acquire the lock in write mode.
void TRWSpinLock::WriteLock()
{
   ++fWriterReservation;

   std::unique_lock<ROOT::TSpinMutex> lock(fMutex);

   // Wait for other writers, if any
   fCond.wait(lock, [this]{ return !fWriter; });

   // Claim the lock for this writer
   fWriter = true;

   // Wait until all reader reservations finish
   while(fReaderReservation) {};

   // Wait for remaining readers
   fCond.wait(lock, [this]{ return fReaders == 0; });

   --fWriterReservation;

   lock.unlock();
}

//////////////////////////////////////////////////////////////////////////
/// Release the lock in write mode.
void TRWSpinLock::WriteUnLock()
{
   // We need to lock here to prevent interleaving with a reader
   std::lock_guard<ROOT::TSpinMutex> lock(fMutex);

   fWriter = false;

   // Notify all potential readers/writers that are waiting
   fCond.notify_all();
}

<commit_msg>Get TRWSpinLock header from the ROOT subdir<commit_after>// @(#)root/thread:$Id$
// Authors: Enric Tejedor CERN  12/09/2016
//          Philippe Canal FNAL 12/09/2016

/*************************************************************************
 * Copyright (C) 1995-2016, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

/** \class TRWSpinLock
    \brief An implementation of a read-write lock with an internal spin lock.

This class provides an implementation of a read-write lock that uses an
internal spin lock and a condition variable to synchronize readers and
writers when necessary.

The implementation tries to make faster the scenario when readers come
and go but there is no writer. In that case, readers will not pay the
price of taking the internal spin lock.

Moreover, this RW lock tries to be fair with writers, giving them the
possibility to claim the lock and wait for only the remaining readers,
thus preventing starvation.
*/

#include "ROOT/TRWSpinLock.h"

using namespace ROOT;

////////////////////////////////////////////////////////////////////////////
/// Acquire the lock in read mode.
void TRWSpinLock::ReadLock()
{
  ++fReaderReservation;
   if (!fWriter) {
      // There is no writer, go freely to the critical section
      ++fReaders;
      --fReaderReservation;
   } else {
      // A writer claimed the RW lock, we will need to wait on the
      // internal lock
      --fReaderReservation;

      std::unique_lock<ROOT::TSpinMutex> lock(fMutex);

      // Wait for writers, if any
      fCond.wait(lock, [this]{ return !fWriter; });

      // This RW lock now belongs to the readers
      ++fReaders;

      lock.unlock();
   }
}

//////////////////////////////////////////////////////////////////////////
/// Release the lock in read mode.
void TRWSpinLock::ReadUnLock()
{
   --fReaders;
   if (fWriterReservation && fReaders == 0) {
      // We still need to lock here to prevent interleaving with a writer
      std::lock_guard<ROOT::TSpinMutex> lock(fMutex);

      // Make sure you wake up a writer, if any
      // Note: spurrious wakeups are okay, fReaders
      // will be checked again in WriteLock
      fCond.notify_all();
   }
}

//////////////////////////////////////////////////////////////////////////
/// Acquire the lock in write mode.
void TRWSpinLock::WriteLock()
{
   ++fWriterReservation;

   std::unique_lock<ROOT::TSpinMutex> lock(fMutex);

   // Wait for other writers, if any
   fCond.wait(lock, [this]{ return !fWriter; });

   // Claim the lock for this writer
   fWriter = true;

   // Wait until all reader reservations finish
   while(fReaderReservation) {};

   // Wait for remaining readers
   fCond.wait(lock, [this]{ return fReaders == 0; });

   --fWriterReservation;

   lock.unlock();
}

//////////////////////////////////////////////////////////////////////////
/// Release the lock in write mode.
void TRWSpinLock::WriteUnLock()
{
   // We need to lock here to prevent interleaving with a reader
   std::lock_guard<ROOT::TSpinMutex> lock(fMutex);

   fWriter = false;

   // Notify all potential readers/writers that are waiting
   fCond.notify_all();
}

<|endoftext|>


<commit_before>// Copyright 2016 Samuel Austin
//
// 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 <Windows.h>

// Internal Includes
#include <osvr/PluginKit/PluginKit.h>
#include <osvr/PluginKit/TrackerInterfaceC.h>

// Generated JSON header file
#include "com_samaust_trackerv2_osvr_json.h"

// Standard includes
#include <iostream>
#include <cmath>

#include "trackir.h"

#define DEGTORADDIV2 0.00872664625997165

trackir::trackir(OSVR_PluginRegContext ctx, CameraLibrary::Camera *camera)
{
	std::cout << "[TrackerV2-OSVR] Initializing TrackIR..." << std::endl;

	m_camera = camera;

	// Set Video Mode
	// TrackIR 4 only supports segment mode
	// TrackIR 5 supports segment mode, Interleaved grayscale mode - non-realtime and Bit Packed Precision mode
	m_camera->SetVideoType(Core::SegmentMode);

	// Start camera output
	m_camera->Start();

	// CameraLibrary objects
	m_vec = CameraLibrary::cModuleVector::Create(); //new cModuleVector();
	m_vecprocessor = new CameraLibrary::cModuleVectorProcessing();
	CameraLibrary::cVectorProcessingSettings vectorProcessorSettings;
	CameraLibrary::cVectorSettings vectorSettings;

	// Get Vector arrangement from settings (currently hardcoded to TrackClipPro)
	//CameraLibrary::cVectorSettings::Arrangements selectedArrangement = CameraLibrary::cVectorSettings::VectorClip;
	CameraLibrary::cVectorSettings::Arrangements selectedArrangement = CameraLibrary::cVectorSettings::TrackClipPro;

	// Configure vectorProcessorSettings
	vectorProcessorSettings = *m_vecprocessor->Settings();

	vectorProcessorSettings.Arrangement = selectedArrangement;
	vectorProcessorSettings.ShowPivotPoint = false;
	vectorProcessorSettings.ShowProcessed = false;

	m_vecprocessor->SetSettings(vectorProcessorSettings);

	// Get camera lens distortion
	m_camera->GetDistortionModel(m_lensDistortion);

	// Configure vector settings
	vectorSettings = *m_vec->Settings();
	vectorSettings.Arrangement = selectedArrangement;
	vectorSettings.Enabled = true;

	// Plug in focal length in (mm) by converting it from pixels -> mm
	vectorSettings.ImagerFocalLength = (m_lensDistortion.HorizontalFocalLength / ((float)m_camera->PhysicalPixelWidth()))*m_camera->ImagerWidth();

	vectorSettings.ImagerHeight = m_camera->ImagerHeight();
	vectorSettings.ImagerWidth = m_camera->ImagerWidth();

	vectorSettings.PrincipalX = m_camera->PhysicalPixelWidth() / 2;
	vectorSettings.PrincipalY = m_camera->PhysicalPixelHeight() / 2;

	vectorSettings.PixelWidth = m_camera->PhysicalPixelWidth();
	vectorSettings.PixelHeight = m_camera->PhysicalPixelHeight();

	m_vec->SetSettings(vectorSettings);

	// Turn off TrackIR illumination LEDs (not necessary for TrackClip PRO)
	m_camera->SetLED(CameraLibrary::eStatusLEDs::IlluminationLED, false);

	std::cout << "[TrackerV2-OSVR] TrackIR initialized" << std::endl;

	// Create the initialization options
	OSVR_DeviceInitOptions opts = osvrDeviceCreateInitOptions(ctx);

	// configure our tracker
	osvrDeviceTrackerConfigure(opts, &m_tracker);

	// Create the device token with the options
	m_dev.initAsync(ctx, "Tracker", opts);

	// Send JSON descriptor
	m_dev.sendJsonDescriptor(com_samaust_trackerv2_osvr_json);

	// Register update callback
	m_dev.registerUpdateCallback(this);
}

trackir::~trackir(void)
{
	// Release camera
	m_camera->Release();

	// Shutdown Camera Library
	CameraLibrary::CameraManager::X().Shutdown();
}

OSVR_ReturnCode trackir::update() {
	// Fetch a new frame from the camera
	CameraLibrary::Frame *frame = m_camera->GetFrame();

	if (frame)
	{
		m_vec->BeginFrame();

		for (int i = 0; i<frame->ObjectCount(); i++)
		{
			CameraLibrary::cObject *obj = frame->Object(i);

			float x = obj->X();
			float y = obj->Y();

			Core::Undistort2DPoint(m_lensDistortion, x, y);

			m_vec->PushMarkerData(x, y, obj->Area(), obj->Width(), obj->Height());
		}
		m_vec->Calculate();
		m_vecprocessor->PushData(m_vec);

		// Recenter if CTRL + F12 is pressed
		if (GetAsyncKeyState(VK_CONTROL) & 0x8000)
		{
			if (GetAsyncKeyState(VK_F12) & 0x8000)
				m_vecprocessor->Recenter();
		}

		// Get position and orientation
		m_vecprocessor->GetPosition(m_x, m_y, m_z);
		m_vecprocessor->GetOrientation(m_yaw, m_pitch, m_roll);

		// Release frame
		frame->Release();
	}

	// Euler to quaternion
	// Source : http://www.euclideanspace.com/maths/geometry/rotations/conversions/eulerToQuaternion/
	double c1 = cos(m_yaw * DEGTORADDIV2);
	double s1 = sin(m_yaw * DEGTORADDIV2);
	double c2 = cos(m_pitch * DEGTORADDIV2);
	double s2 = sin(m_pitch * DEGTORADDIV2);
	double c3 = cos(-m_roll * DEGTORADDIV2);
	double s3 = sin(-m_roll * DEGTORADDIV2);
	double c1c2 = c1*c2;
	double s1s2 = s1*s2;

	// Report pose
	OSVR_Pose3 pose;
	pose.translation.data[0] = m_x;
	pose.translation.data[1] = m_y;
	pose.translation.data[2] = m_z;
	pose.rotation.data[0] = c1c2*c3 - s1s2*s3;
	pose.rotation.data[1] = c1c2*s3 + s1s2*c3;
	pose.rotation.data[2] = s1*c2*c3 + c1*s2*s3;
	pose.rotation.data[3] = c1*s2*c3 - s1*c2*s3;
	osvrDeviceTrackerSendPose(m_dev, m_tracker, &pose, 0);

	return OSVR_RETURN_SUCCESS;
}
<commit_msg>Turn on green LED if three or more targets are detected by the camera<commit_after>// Copyright 2016 Samuel Austin
//
// 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 <Windows.h>

// Internal Includes
#include <osvr/PluginKit/PluginKit.h>
#include <osvr/PluginKit/TrackerInterfaceC.h>

// Generated JSON header file
#include "com_samaust_trackerv2_osvr_json.h"

// Standard includes
#include <iostream>
#include <cmath>

#include "trackir.h"

#define DEGTORADDIV2 0.00872664625997165

trackir::trackir(OSVR_PluginRegContext ctx, CameraLibrary::Camera *camera)
{
	std::cout << "[TrackerV2-OSVR] Initializing TrackIR..." << std::endl;

	m_camera = camera;

	// Set Video Mode
	// TrackIR 4 only supports segment mode
	// TrackIR 5 supports segment mode, Interleaved grayscale mode - non-realtime and Bit Packed Precision mode
	m_camera->SetVideoType(Core::SegmentMode);

	// Start camera output
	m_camera->Start();

	// CameraLibrary objects
	m_vec = CameraLibrary::cModuleVector::Create(); //new cModuleVector();
	m_vecprocessor = new CameraLibrary::cModuleVectorProcessing();
	CameraLibrary::cVectorProcessingSettings vectorProcessorSettings;
	CameraLibrary::cVectorSettings vectorSettings;

	// Get Vector arrangement from settings (currently hardcoded to TrackClipPro)
	//CameraLibrary::cVectorSettings::Arrangements selectedArrangement = CameraLibrary::cVectorSettings::VectorClip;
	CameraLibrary::cVectorSettings::Arrangements selectedArrangement = CameraLibrary::cVectorSettings::TrackClipPro;

	// Configure vectorProcessorSettings
	vectorProcessorSettings = *m_vecprocessor->Settings();

	vectorProcessorSettings.Arrangement = selectedArrangement;
	vectorProcessorSettings.ShowPivotPoint = false;
	vectorProcessorSettings.ShowProcessed = false;

	m_vecprocessor->SetSettings(vectorProcessorSettings);

	// Get camera lens distortion
	m_camera->GetDistortionModel(m_lensDistortion);

	// Configure vector settings
	vectorSettings = *m_vec->Settings();
	vectorSettings.Arrangement = selectedArrangement;
	vectorSettings.Enabled = true;

	// Plug in focal length in (mm) by converting it from pixels -> mm
	vectorSettings.ImagerFocalLength = (m_lensDistortion.HorizontalFocalLength / ((float)m_camera->PhysicalPixelWidth()))*m_camera->ImagerWidth();

	vectorSettings.ImagerHeight = m_camera->ImagerHeight();
	vectorSettings.ImagerWidth = m_camera->ImagerWidth();

	vectorSettings.PrincipalX = m_camera->PhysicalPixelWidth() / 2;
	vectorSettings.PrincipalY = m_camera->PhysicalPixelHeight() / 2;

	vectorSettings.PixelWidth = m_camera->PhysicalPixelWidth();
	vectorSettings.PixelHeight = m_camera->PhysicalPixelHeight();

	m_vec->SetSettings(vectorSettings);

	// Turn off TrackIR illumination LEDs (not necessary for TrackClip PRO)
	m_camera->SetLED(CameraLibrary::eStatusLEDs::IlluminationLED, false);

	std::cout << "[TrackerV2-OSVR] TrackIR initialized" << std::endl;

	// Create the initialization options
	OSVR_DeviceInitOptions opts = osvrDeviceCreateInitOptions(ctx);

	// configure our tracker
	osvrDeviceTrackerConfigure(opts, &m_tracker);

	// Create the device token with the options
	m_dev.initAsync(ctx, "Tracker", opts);

	// Send JSON descriptor
	m_dev.sendJsonDescriptor(com_samaust_trackerv2_osvr_json);

	// Register update callback
	m_dev.registerUpdateCallback(this);
}

trackir::~trackir(void)
{
	// Release camera
	m_camera->Release();

	// Shutdown Camera Library
	CameraLibrary::CameraManager::X().Shutdown();
}

OSVR_ReturnCode trackir::update() {
	// Fetch a new frame from the camera
	CameraLibrary::Frame *frame = m_camera->GetFrame();

	if (frame)
	{
		m_vec->BeginFrame();
		int frameObjectCount = frame->ObjectCount();

		// Light green LED if TracClip PRO is detected
		if (frameObjectCount >= 3)
			m_camera->SetLED(CameraLibrary::eStatusLEDs::GreenStatusLED, true);
		else
			m_camera->SetLED(CameraLibrary::eStatusLEDs::GreenStatusLED, false);

		for (int i = 0; i<frameObjectCount; i++)
		{
			CameraLibrary::cObject *obj = frame->Object(i);

			float x = obj->X();
			float y = obj->Y();

			Core::Undistort2DPoint(m_lensDistortion, x, y);

			m_vec->PushMarkerData(x, y, obj->Area(), obj->Width(), obj->Height());
		}
		m_vec->Calculate();
		m_vecprocessor->PushData(m_vec);

		// Recenter if CTRL + F12 is pressed
		if (GetAsyncKeyState(VK_CONTROL) & 0x8000)
		{
			if (GetAsyncKeyState(VK_F12) & 0x8000)
				m_vecprocessor->Recenter();
		}

		// Get position and orientation
		m_vecprocessor->GetPosition(m_x, m_y, m_z);
		m_vecprocessor->GetOrientation(m_yaw, m_pitch, m_roll);

		// Release frame
		frame->Release();
	}

	// Euler to quaternion
	// Source : http://www.euclideanspace.com/maths/geometry/rotations/conversions/eulerToQuaternion/
	double c1 = cos(m_yaw * DEGTORADDIV2);
	double s1 = sin(m_yaw * DEGTORADDIV2);
	double c2 = cos(m_pitch * DEGTORADDIV2);
	double s2 = sin(m_pitch * DEGTORADDIV2);
	double c3 = cos(-m_roll * DEGTORADDIV2);
	double s3 = sin(-m_roll * DEGTORADDIV2);
	double c1c2 = c1*c2;
	double s1s2 = s1*s2;

	// Report pose
	OSVR_Pose3 pose;
	pose.translation.data[0] = m_x;
	pose.translation.data[1] = m_y;
	pose.translation.data[2] = m_z;
	pose.rotation.data[0] = c1c2*c3 - s1s2*s3;
	pose.rotation.data[1] = c1c2*s3 + s1s2*c3;
	pose.rotation.data[2] = s1*c2*c3 + c1*s2*s3;
	pose.rotation.data[3] = c1*s2*c3 - s1*c2*s3;
	osvrDeviceTrackerSendPose(m_dev, m_tracker, &pose, 0);

	return OSVR_RETURN_SUCCESS;
}
<|endoftext|>


<commit_before>#include "ajokki_callbacks.hpp"
#include "cpp/ylikuutio/callback/callback_parameter.hpp"
#include "cpp/ylikuutio/callback/callback_object.hpp"
#include "cpp/ylikuutio/callback/callback_engine.hpp"
#include "cpp/ylikuutio/common/any_value.hpp"
#include "cpp/ylikuutio/ontology/text2D.hpp"
#include "cpp/ylikuutio/ontology/object.hpp"
#include "cpp/ylikuutio/ontology/species.hpp"
#include "cpp/ylikuutio/ontology/material.hpp"
#include "cpp/ylikuutio/ontology/universe.hpp"

// Include GLFW
#ifndef __GLFW3_H_INCLUDED
#define __GLFW3_H_INCLUDED
#include <glfw3.h>
#endif

// Include standard headers
#include <iostream> // std::cout, std::cin, std::cerr
#include <vector>   // std::vector

namespace ajokki
{
    datatypes::AnyValue* glfwTerminate_cleanup(
            callback_system::CallbackEngine*,
            callback_system::CallbackObject*,
            std::vector<callback_system::CallbackParameter*> input_parameters)
    {
        glfwTerminate();
        return nullptr;
    }

    datatypes::AnyValue* full_cleanup(
            callback_system::CallbackEngine*,
            callback_system::CallbackObject*,
            std::vector<callback_system::CallbackParameter*> input_parameters)
    {
        std::cout << "Cleaning up.\n";

        if (input_parameters.size() != 1)
        {
            std::cerr << "Invalid size of input_parameters: " << input_parameters.size() << ", should be 1.\n";
        }
        else
        {
            datatypes::AnyValue* any_value = input_parameters.at(0)->get_any_value();
            if (any_value->type == datatypes::VOID_POINTER)
            {
                delete static_cast<ontology::Universe*>(any_value->void_pointer);
            }
            else
            {
                std::cerr << "Invalid datatype: " << any_value->type << ", should be " << datatypes::VOID_POINTER << "\n";
            }
        }

        // Delete the text's VBO, the shader and the texture
        text2D::cleanupText2D();

        // Close OpenGL window and terminate GLFW
        glfwTerminate();
        return nullptr;
    }

    datatypes::AnyValue* delete_suzanne_species(
            callback_system::CallbackEngine*,
            callback_system::CallbackObject*,
            std::vector<callback_system::CallbackParameter*> input_parameters)
    {
        datatypes::AnyValue* any_value = input_parameters.at(1)->get_any_value();

        if (any_value->type != datatypes::BOOL_POINTER)
        {
            // `any_value` was not `bool*`.
            return nullptr;
        }

        // OK, `any_value` is a `bool*`.
        bool* does_suzanne_species_exist = any_value->bool_pointer;

        if (*does_suzanne_species_exist)
        {
            ontology::Species* species = static_cast<ontology::Species*>(input_parameters.at(0)->get_any_value()->void_pointer);
            delete species;

            *does_suzanne_species_exist = false;
        }
        return nullptr;
    }

    datatypes::AnyValue* switch_to_new_material(
            callback_system::CallbackEngine*,
            callback_system::CallbackObject*,
            std::vector<callback_system::CallbackParameter*> input_parameters)
    {
        bool* does_suzanne_species_exist = static_cast<bool*>(input_parameters.at(2)->get_any_value()->void_pointer);
        bool* does_suzanne_species_have_original_texture = static_cast<bool*>(input_parameters.at(3)->get_any_value()->void_pointer);
        bool* does_suzanne_species_have_new_texture = static_cast<bool*>(input_parameters.at(4)->get_any_value()->void_pointer);

        if (*does_suzanne_species_exist && *does_suzanne_species_have_original_texture)
        {
            ontology::Species* species = static_cast<ontology::Species*>(input_parameters.at(0)->get_any_value()->void_pointer);
            ontology::Material* material = static_cast<ontology::Material*>(input_parameters.at(1)->get_any_value()->void_pointer);
            species->bind_to_new_parent(material);

            *does_suzanne_species_have_original_texture = false;
            *does_suzanne_species_have_new_texture = true;
        }
        return nullptr;
    }

    datatypes::AnyValue* transform_into_new_species(
            callback_system::CallbackEngine*,
            callback_system::CallbackObject*,
            std::vector<callback_system::CallbackParameter*> input_parameters)
    {
        bool* does_suzanne_species_exist = static_cast<bool*>(input_parameters.at(2)->get_any_value()->void_pointer);
        bool* does_suzanne_species_belong_to_original_species = static_cast<bool*>(input_parameters.at(3)->get_any_value()->void_pointer);
        bool* does_suzanne_species_belong_to_new_species = static_cast<bool*>(input_parameters.at(4)->get_any_value()->void_pointer);

        if (*does_suzanne_species_exist && *does_suzanne_species_belong_to_original_species)
        {
            ontology::Object* object = static_cast<ontology::Object*>(input_parameters.at(0)->get_any_value()->void_pointer);
            ontology::Species* species = static_cast<ontology::Species*>(input_parameters.at(1)->get_any_value()->void_pointer);
            object->bind_to_new_parent(species);

            *does_suzanne_species_belong_to_original_species = false;
            *does_suzanne_species_belong_to_new_species = true;
        }
        return nullptr;
    }
}

<commit_msg>Renamed variables.<commit_after>#include "ajokki_callbacks.hpp"
#include "cpp/ylikuutio/callback/callback_parameter.hpp"
#include "cpp/ylikuutio/callback/callback_object.hpp"
#include "cpp/ylikuutio/callback/callback_engine.hpp"
#include "cpp/ylikuutio/common/any_value.hpp"
#include "cpp/ylikuutio/ontology/text2D.hpp"
#include "cpp/ylikuutio/ontology/object.hpp"
#include "cpp/ylikuutio/ontology/species.hpp"
#include "cpp/ylikuutio/ontology/material.hpp"
#include "cpp/ylikuutio/ontology/universe.hpp"

// Include GLFW
#ifndef __GLFW3_H_INCLUDED
#define __GLFW3_H_INCLUDED
#include <glfw3.h>
#endif

// Include standard headers
#include <iostream> // std::cout, std::cin, std::cerr
#include <vector>   // std::vector

namespace ajokki
{
    datatypes::AnyValue* glfwTerminate_cleanup(
            callback_system::CallbackEngine*,
            callback_system::CallbackObject*,
            std::vector<callback_system::CallbackParameter*> input_parameters)
    {
        glfwTerminate();
        return nullptr;
    }

    datatypes::AnyValue* full_cleanup(
            callback_system::CallbackEngine*,
            callback_system::CallbackObject*,
            std::vector<callback_system::CallbackParameter*> input_parameters)
    {
        std::cout << "Cleaning up.\n";

        if (input_parameters.size() != 1)
        {
            std::cerr << "Invalid size of input_parameters: " << input_parameters.size() << ", should be 1.\n";
        }
        else
        {
            datatypes::AnyValue* any_value_void_pointer = input_parameters.at(0)->get_any_value();
            if (any_value_void_pointer->type == datatypes::VOID_POINTER)
            {
                delete static_cast<ontology::Universe*>(any_value_void_pointer->void_pointer);
            }
            else
            {
                std::cerr << "Invalid datatype: " << any_value_void_pointer->type << ", should be " << datatypes::VOID_POINTER << "\n";
            }
        }

        // Delete the text's VBO, the shader and the texture
        text2D::cleanupText2D();

        // Close OpenGL window and terminate GLFW
        glfwTerminate();
        return nullptr;
    }

    datatypes::AnyValue* delete_suzanne_species(
            callback_system::CallbackEngine*,
            callback_system::CallbackObject*,
            std::vector<callback_system::CallbackParameter*> input_parameters)
    {
        datatypes::AnyValue* any_value_bool_pointer = input_parameters.at(1)->get_any_value();

        if (any_value_bool_pointer->type != datatypes::BOOL_POINTER)
        {
            // `any_value_bool_pointer` was not `bool*`.
            return nullptr;
        }

        // OK, `any_value_bool_pointer` is a `bool*`.
        bool* does_suzanne_species_exist = any_value_bool_pointer->bool_pointer;

        if (*does_suzanne_species_exist)
        {
            ontology::Species* species = static_cast<ontology::Species*>(input_parameters.at(0)->get_any_value()->void_pointer);
            delete species;

            *does_suzanne_species_exist = false;
        }
        return nullptr;
    }

    datatypes::AnyValue* switch_to_new_material(
            callback_system::CallbackEngine*,
            callback_system::CallbackObject*,
            std::vector<callback_system::CallbackParameter*> input_parameters)
    {
        bool* does_suzanne_species_exist = static_cast<bool*>(input_parameters.at(2)->get_any_value()->void_pointer);
        bool* does_suzanne_species_have_original_texture = static_cast<bool*>(input_parameters.at(3)->get_any_value()->void_pointer);
        bool* does_suzanne_species_have_new_texture = static_cast<bool*>(input_parameters.at(4)->get_any_value()->void_pointer);

        if (*does_suzanne_species_exist && *does_suzanne_species_have_original_texture)
        {
            ontology::Species* species = static_cast<ontology::Species*>(input_parameters.at(0)->get_any_value()->void_pointer);
            ontology::Material* material = static_cast<ontology::Material*>(input_parameters.at(1)->get_any_value()->void_pointer);
            species->bind_to_new_parent(material);

            *does_suzanne_species_have_original_texture = false;
            *does_suzanne_species_have_new_texture = true;
        }
        return nullptr;
    }

    datatypes::AnyValue* transform_into_new_species(
            callback_system::CallbackEngine*,
            callback_system::CallbackObject*,
            std::vector<callback_system::CallbackParameter*> input_parameters)
    {
        bool* does_suzanne_species_exist = static_cast<bool*>(input_parameters.at(2)->get_any_value()->void_pointer);
        bool* does_suzanne_species_belong_to_original_species = static_cast<bool*>(input_parameters.at(3)->get_any_value()->void_pointer);
        bool* does_suzanne_species_belong_to_new_species = static_cast<bool*>(input_parameters.at(4)->get_any_value()->void_pointer);

        if (*does_suzanne_species_exist && *does_suzanne_species_belong_to_original_species)
        {
            ontology::Object* object = static_cast<ontology::Object*>(input_parameters.at(0)->get_any_value()->void_pointer);
            ontology::Species* species = static_cast<ontology::Species*>(input_parameters.at(1)->get_any_value()->void_pointer);
            object->bind_to_new_parent(species);

            *does_suzanne_species_belong_to_original_species = false;
            *does_suzanne_species_belong_to_new_species = true;
        }
        return nullptr;
    }
}

<|endoftext|>


<commit_before>#ifndef FMO_RETAINER_HPP
#define FMO_RETAINER_HPP

#include <array>
#include <vector>

namespace fmo {
    /// A container for objects of type T, which must be default constructible and also must have a
    /// method named clear() which reverts the object to a state equivalent to that after default
    /// construction.
    ///
    /// The first Count elements are never destroyed, clear() is called on them instead.
    template <typename T, size_t Count>
    struct Retainer {
        Retainer() = default;
        Retainer(const Retainer&) = default;
        Retainer(Retainer&&) = default;
        Retainer& operator=(const Retainer&) = default;
        Retainer& operator=(Retainer&&) = default;

        void swap(Retainer& rhs) {
            std::swap(mSz, rhs.mSz);
            mArr.swap(rhs.mArr);
            mVec.swap(rhs.mVec);
        }
        friend void swap(Retainer& lhs, Retainer& rhs) { lhs.swap(rhs); }

        bool empty() const { return mSz == 0; }
        size_t size() const { return mSz; }

        void emplace_back() {
            if (++mSz > Count) { mVec.emplace_back(); }
        }

        void pop_back() {
            if (mSz-- > Count) {
                mVec.pop_back();
            } else {
                mArr[mSz].clear();
            }
        }

        T& back() {
            if (mSz > Count) return mVec.back();
            return mArr[mSz - 1];
        }

        const T& back() const {
            if (mSz > Count) return mVec.back();
            return mArr[mSz - 1];
        }

        void clear() {
            if (mSz > Count) {
                mVec.clear();
                mSz = Count;
            }
            while (!empty()) pop_back();
        }

    private:
        std::array<T, Count> mArr;
        std::vector<T> mVec;
        size_t mSz = 0;
    };
}

#endif // FMO_RETAINER_HPP
<commit_msg>Keep the data order in swap()<commit_after>#ifndef FMO_RETAINER_HPP
#define FMO_RETAINER_HPP

#include <array>
#include <vector>

namespace fmo {
    /// A container for objects of type T, which must be default constructible and also must have a
    /// method named clear() which reverts the object to a state equivalent to that after default
    /// construction.
    ///
    /// The first Count elements are never destroyed, clear() is called on them instead.
    template <typename T, size_t Count>
    struct Retainer {
        Retainer() = default;
        Retainer(const Retainer&) = default;
        Retainer(Retainer&&) = default;
        Retainer& operator=(const Retainer&) = default;
        Retainer& operator=(Retainer&&) = default;

        void swap(Retainer& rhs) {
            mArr.swap(rhs.mArr);
            mVec.swap(rhs.mVec);
            std::swap(mSz, rhs.mSz);
        }
        friend void swap(Retainer& lhs, Retainer& rhs) { lhs.swap(rhs); }

        bool empty() const { return mSz == 0; }
        size_t size() const { return mSz; }

        void emplace_back() {
            if (++mSz > Count) { mVec.emplace_back(); }
        }

        void pop_back() {
            if (mSz-- > Count) {
                mVec.pop_back();
            } else {
                mArr[mSz].clear();
            }
        }

        T& back() {
            if (mSz > Count) return mVec.back();
            return mArr[mSz - 1];
        }

        const T& back() const {
            if (mSz > Count) return mVec.back();
            return mArr[mSz - 1];
        }

        void clear() {
            if (mSz > Count) {
                mVec.clear();
                mSz = Count;
            }
            while (!empty()) pop_back();
        }

    private:
        std::array<T, Count> mArr;
        std::vector<T> mVec;
        size_t mSz = 0;
    };
}

#endif // FMO_RETAINER_HPP
<|endoftext|>


<commit_before>// Copyright 2017 Global Phasing Ltd.

#ifndef GEMMI_TO_CIF_HPP_
#define GEMMI_TO_CIF_HPP_

#include <fstream>
#include "cifdoc.hpp"

namespace gemmi {
namespace cif {

enum class Style {
  Simple,
  NoBlankLines,
  PreferPairs,  // write single-row loops as pairs
  Pdbx,         // PreferPairs + put '#' (empty comments) between categories
  Indent35,     // start values in pairs from 35th column
};

// CIF files are read in binary mode. It makes difference only for text fields.
// If the text field with \r\n would be written as is in text mode on Windows
// \r would get duplicated. As a workaround, here we convert \r\n to \n.
// Hopefully \r that gets removed here is never meaningful.
inline void write_text_field(std::ostream& os, const std::string& value) {
  for (size_t pos = 0, end = 0; end != std::string::npos; pos = end + 1) {
    end = value.find("\r\n", pos);
    size_t len = (end == std::string::npos ? value.size() : end) - pos;
    os.write(value.c_str() + pos, len);
  }
}

inline void write_out_pair(std::ostream& os, const std::string& name,
                           const std::string& value, Style style) {
  os << name;
  if (is_text_field(value)) {
    os.put('\n');
    write_text_field(os, value);
  } else {
    if (name.size() + value.size() > 120)
      os.put('\n');
    else if (style == Style::Indent35 && name.size() < 34)
      os.write("                                  ", 34 - name.size());
    else
      os.put(' ');
    os << value;
  }
  os.put('\n');
}

inline void write_out_loop(std::ostream& os, const Loop& loop, Style style) {
  if (loop.values.empty())
    return;
  if ((style == Style::PreferPairs || style == Style::Pdbx) &&
      loop.length() == 1) {
    for (size_t i = 0; i != loop.tags.size(); ++i)
      write_out_pair(os, loop.tags[i], loop.values[i], style);
    return;
  }
  os << "loop_";
  for (const std::string& tag : loop.tags)
    os << '\n' << tag;
  size_t ncol = loop.tags.size();
  size_t col = 0;
  for (const std::string& val : loop.values) {
    bool text_field = is_text_field(val);
    os.put(col++ == 0 || text_field ? '\n' : ' ');
    if (text_field)
      write_text_field(os, val);
    else
      os << val;
    if (col == ncol)
      col = 0;
  }
  os.put('\n');
}

inline void write_out_item(std::ostream& os, const Item& item, Style style) {
  switch (item.type) {
    case ItemType::Pair:
      write_out_pair(os, item.pair[0], item.pair[1], style);
      break;
    case ItemType::Loop:
      write_out_loop(os, item.loop, style);
      break;
    case ItemType::Frame:
      os << "save_" << item.frame.name << '\n';
      for (const Item& inner_item : item.frame.items)
        write_out_item(os, inner_item, style);
      os << "save_\n";
      break;
    case ItemType::Comment:
      os << item.pair[1] << '\n';
      break;
    case ItemType::Erased:
      break;
  }
}

inline bool should_be_separted_(const Item& a, const Item& b) {
  if (a.type == ItemType::Comment || b.type == ItemType::Comment)
    return false;
  if (a.type != ItemType::Pair || b.type != ItemType::Pair)
    return true;
  // check if we have mmcif-like tags from different categories
  auto adot = a.pair[0].find('.');
  if (adot == std::string::npos)
    return false;
  auto bdot = b.pair[0].find('.');
  return adot != bdot || a.pair[0].compare(0, adot, b.pair[0], 0, adot) != 0;
}

inline void write_cif_to_stream(std::ostream& os, const Document& doc,
                                Style s=Style::Simple) {
  bool first = true;
  for (const Block& block : doc.blocks) {
    if (!first)
      os.put('\n'); // extra blank line for readability
    os << "data_" << block.name << '\n';
    if (s == Style::Pdbx)
      os << "#\n";
    const Item* prev = nullptr;
    for (const Item& item : block.items)
      if (item.type != ItemType::Erased) {
        if (prev && s != Style::NoBlankLines &&
            should_be_separted_(*prev, item))
          os << (s == Style::Pdbx ? "#\n" : "\n");
        write_out_item(os, item, s);
        prev = &item;
      }
    first = false;
    if (s == Style::Pdbx)
      os << "#\n";
  }
}

inline void write_cif_to_file(const Document& doc, const std::string& filename,
                          Style s=Style::Simple) {
  std::ofstream of(filename);
  if (!of)
    throw std::runtime_error("Failed to open " + filename);
  write_cif_to_stream(of, doc, s);
  of.close();
}

} // namespace cif
} // namespace gemmi

#endif
<commit_msg>write_cif_to_file(): support non-ascii filenames on Windows (MSVC only)<commit_after>// Copyright 2017 Global Phasing Ltd.

#ifndef GEMMI_TO_CIF_HPP_
#define GEMMI_TO_CIF_HPP_

#include <fstream>
#include "cifdoc.hpp"

#if defined(_MSC_VER) && !defined(GEMMI_USE_FOPEN)
#include <locale>
#include <codecvt>
#endif

namespace gemmi {
namespace cif {

enum class Style {
  Simple,
  NoBlankLines,
  PreferPairs,  // write single-row loops as pairs
  Pdbx,         // PreferPairs + put '#' (empty comments) between categories
  Indent35,     // start values in pairs from 35th column
};

// CIF files are read in binary mode. It makes difference only for text fields.
// If the text field with \r\n would be written as is in text mode on Windows
// \r would get duplicated. As a workaround, here we convert \r\n to \n.
// Hopefully \r that gets removed here is never meaningful.
inline void write_text_field(std::ostream& os, const std::string& value) {
  for (size_t pos = 0, end = 0; end != std::string::npos; pos = end + 1) {
    end = value.find("\r\n", pos);
    size_t len = (end == std::string::npos ? value.size() : end) - pos;
    os.write(value.c_str() + pos, len);
  }
}

inline void write_out_pair(std::ostream& os, const std::string& name,
                           const std::string& value, Style style) {
  os << name;
  if (is_text_field(value)) {
    os.put('\n');
    write_text_field(os, value);
  } else {
    if (name.size() + value.size() > 120)
      os.put('\n');
    else if (style == Style::Indent35 && name.size() < 34)
      os.write("                                  ", 34 - name.size());
    else
      os.put(' ');
    os << value;
  }
  os.put('\n');
}

inline void write_out_loop(std::ostream& os, const Loop& loop, Style style) {
  if (loop.values.empty())
    return;
  if ((style == Style::PreferPairs || style == Style::Pdbx) &&
      loop.length() == 1) {
    for (size_t i = 0; i != loop.tags.size(); ++i)
      write_out_pair(os, loop.tags[i], loop.values[i], style);
    return;
  }
  os << "loop_";
  for (const std::string& tag : loop.tags)
    os << '\n' << tag;
  size_t ncol = loop.tags.size();
  size_t col = 0;
  for (const std::string& val : loop.values) {
    bool text_field = is_text_field(val);
    os.put(col++ == 0 || text_field ? '\n' : ' ');
    if (text_field)
      write_text_field(os, val);
    else
      os << val;
    if (col == ncol)
      col = 0;
  }
  os.put('\n');
}

inline void write_out_item(std::ostream& os, const Item& item, Style style) {
  switch (item.type) {
    case ItemType::Pair:
      write_out_pair(os, item.pair[0], item.pair[1], style);
      break;
    case ItemType::Loop:
      write_out_loop(os, item.loop, style);
      break;
    case ItemType::Frame:
      os << "save_" << item.frame.name << '\n';
      for (const Item& inner_item : item.frame.items)
        write_out_item(os, inner_item, style);
      os << "save_\n";
      break;
    case ItemType::Comment:
      os << item.pair[1] << '\n';
      break;
    case ItemType::Erased:
      break;
  }
}

inline bool should_be_separted_(const Item& a, const Item& b) {
  if (a.type == ItemType::Comment || b.type == ItemType::Comment)
    return false;
  if (a.type != ItemType::Pair || b.type != ItemType::Pair)
    return true;
  // check if we have mmcif-like tags from different categories
  auto adot = a.pair[0].find('.');
  if (adot == std::string::npos)
    return false;
  auto bdot = b.pair[0].find('.');
  return adot != bdot || a.pair[0].compare(0, adot, b.pair[0], 0, adot) != 0;
}

inline void write_cif_to_stream(std::ostream& os, const Document& doc,
                                Style s=Style::Simple) {
  bool first = true;
  for (const Block& block : doc.blocks) {
    if (!first)
      os.put('\n'); // extra blank line for readability
    os << "data_" << block.name << '\n';
    if (s == Style::Pdbx)
      os << "#\n";
    const Item* prev = nullptr;
    for (const Item& item : block.items)
      if (item.type != ItemType::Erased) {
        if (prev && s != Style::NoBlankLines &&
            should_be_separted_(*prev, item))
          os << (s == Style::Pdbx ? "#\n" : "\n");
        write_out_item(os, item, s);
        prev = &item;
      }
    first = false;
    if (s == Style::Pdbx)
      os << "#\n";
  }
}

inline void write_cif_to_file(const Document& doc, const std::string& filename,
                              Style s=Style::Simple) {
#if defined(_MSC_VER) && !defined(GEMMI_USE_FOPEN)
  std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> convert;
  std::wstring wfilename = convert.from_bytes(filename.c_str());
  std::ofstream of(wfilename.c_str());
#else
  std::ofstream of(filename);
#endif
  if (!of)
    throw std::runtime_error("Failed to open " + filename);
  write_cif_to_stream(of, doc, s);
  of.close();
}

} // namespace cif
} // namespace gemmi

#endif
<|endoftext|>


<commit_before>/*
 *  Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "rtc_base/units/unit_base.h"

#include "test/gtest.h"

namespace webrtc {
namespace {
class TestUnit final : public rtc_units_impl::RelativeUnit<TestUnit> {
 public:
  TestUnit() = delete;

  using UnitBase::FromValue;
  using UnitBase::ToValue;
  using UnitBase::ToValueOr;

  template <typename T>
  static constexpr TestUnit FromKilo(T kilo) {
    return FromFraction(1000, kilo);
  }
  template <typename T = int64_t>
  T ToKilo() const {
    return UnitBase::ToFraction<1000, T>();
  }
  constexpr int64_t ToKiloOr(int64_t fallback) const {
    return UnitBase::ToFractionOr<1000>(fallback);
  }
  template <typename T>
  constexpr T ToMilli() const {
    return UnitBase::ToMultiple<1000, T>();
  }

 private:
  friend class UnitBase<TestUnit>;
  static constexpr bool one_sided = false;
  using RelativeUnit<TestUnit>::RelativeUnit;
};
constexpr TestUnit TestUnitAddKilo(TestUnit value, int add_kilo) {
  value += TestUnit::FromKilo(add_kilo);
  return value;
}
}  // namespace
namespace test {
TEST(UnitBaseTest, ConstExpr) {
  constexpr int64_t kValue = -12345;
  constexpr TestUnit kTestUnitZero = TestUnit::Zero();
  constexpr TestUnit kTestUnitPlusInf = TestUnit::PlusInfinity();
  constexpr TestUnit kTestUnitMinusInf = TestUnit::MinusInfinity();
  static_assert(kTestUnitZero.IsZero(), "");
  static_assert(kTestUnitPlusInf.IsPlusInfinity(), "");
  static_assert(kTestUnitMinusInf.IsMinusInfinity(), "");
  static_assert(kTestUnitPlusInf.ToKiloOr(-1) == -1, "");

  static_assert(kTestUnitPlusInf > kTestUnitZero, "");

  constexpr TestUnit kTestUnitKilo = TestUnit::FromKilo(kValue);
  constexpr TestUnit kTestUnitValue = TestUnit::FromValue(kValue);

  static_assert(kTestUnitKilo.ToKiloOr(0) == kValue, "");
  static_assert(kTestUnitValue.ToValueOr(0) == kValue, "");
  static_assert(TestUnitAddKilo(kTestUnitValue, 2).ToValue() == kValue + 2000,
                "");
}

TEST(UnitBaseTest, GetBackSameValues) {
  const int64_t kValue = 499;
  for (int sign = -1; sign <= 1; ++sign) {
    int64_t value = kValue * sign;
    EXPECT_EQ(TestUnit::FromKilo(value).ToKilo(), value);
    EXPECT_EQ(TestUnit::FromValue(value).ToValue<int64_t>(), value);
  }
  EXPECT_EQ(TestUnit::Zero().ToValue<int64_t>(), 0);
}

TEST(UnitBaseTest, GetDifferentPrefix) {
  const int64_t kValue = 3000000;
  EXPECT_EQ(TestUnit::FromValue(kValue).ToKilo(), kValue / 1000);
  EXPECT_EQ(TestUnit::FromKilo(kValue).ToValue<int64_t>(), kValue * 1000);
}

TEST(UnitBaseTest, IdentityChecks) {
  const int64_t kValue = 3000;
  EXPECT_TRUE(TestUnit::Zero().IsZero());
  EXPECT_FALSE(TestUnit::FromKilo(kValue).IsZero());

  EXPECT_TRUE(TestUnit::PlusInfinity().IsInfinite());
  EXPECT_TRUE(TestUnit::MinusInfinity().IsInfinite());
  EXPECT_FALSE(TestUnit::Zero().IsInfinite());
  EXPECT_FALSE(TestUnit::FromKilo(-kValue).IsInfinite());
  EXPECT_FALSE(TestUnit::FromKilo(kValue).IsInfinite());

  EXPECT_FALSE(TestUnit::PlusInfinity().IsFinite());
  EXPECT_FALSE(TestUnit::MinusInfinity().IsFinite());
  EXPECT_TRUE(TestUnit::FromKilo(-kValue).IsFinite());
  EXPECT_TRUE(TestUnit::FromKilo(kValue).IsFinite());
  EXPECT_TRUE(TestUnit::Zero().IsFinite());

  EXPECT_TRUE(TestUnit::PlusInfinity().IsPlusInfinity());
  EXPECT_FALSE(TestUnit::MinusInfinity().IsPlusInfinity());

  EXPECT_TRUE(TestUnit::MinusInfinity().IsMinusInfinity());
  EXPECT_FALSE(TestUnit::PlusInfinity().IsMinusInfinity());
}

TEST(UnitBaseTest, ComparisonOperators) {
  const int64_t kSmall = 450;
  const int64_t kLarge = 451;
  const TestUnit small = TestUnit::FromKilo(kSmall);
  const TestUnit large = TestUnit::FromKilo(kLarge);

  EXPECT_EQ(TestUnit::Zero(), TestUnit::FromKilo(0));
  EXPECT_EQ(TestUnit::PlusInfinity(), TestUnit::PlusInfinity());
  EXPECT_EQ(small, TestUnit::FromKilo(kSmall));
  EXPECT_LE(small, TestUnit::FromKilo(kSmall));
  EXPECT_GE(small, TestUnit::FromKilo(kSmall));
  EXPECT_NE(small, TestUnit::FromKilo(kLarge));
  EXPECT_LE(small, TestUnit::FromKilo(kLarge));
  EXPECT_LT(small, TestUnit::FromKilo(kLarge));
  EXPECT_GE(large, TestUnit::FromKilo(kSmall));
  EXPECT_GT(large, TestUnit::FromKilo(kSmall));
  EXPECT_LT(TestUnit::Zero(), small);
  EXPECT_GT(TestUnit::Zero(), TestUnit::FromKilo(-kSmall));
  EXPECT_GT(TestUnit::Zero(), TestUnit::FromKilo(-kSmall));

  EXPECT_GT(TestUnit::PlusInfinity(), large);
  EXPECT_LT(TestUnit::MinusInfinity(), TestUnit::Zero());
}

TEST(UnitBaseTest, Clamping) {
  const TestUnit upper = TestUnit::FromKilo(800);
  const TestUnit lower = TestUnit::FromKilo(100);
  const TestUnit under = TestUnit::FromKilo(100);
  const TestUnit inside = TestUnit::FromKilo(500);
  const TestUnit over = TestUnit::FromKilo(1000);
  EXPECT_EQ(under.Clamped(lower, upper), lower);
  EXPECT_EQ(inside.Clamped(lower, upper), inside);
  EXPECT_EQ(over.Clamped(lower, upper), upper);

  TestUnit mutable_delta = lower;
  mutable_delta.Clamp(lower, upper);
  EXPECT_EQ(mutable_delta, lower);
  mutable_delta = inside;
  mutable_delta.Clamp(lower, upper);
  EXPECT_EQ(mutable_delta, inside);
  mutable_delta = over;
  mutable_delta.Clamp(lower, upper);
  EXPECT_EQ(mutable_delta, upper);
}

TEST(UnitBaseTest, CanBeInititializedFromLargeInt) {
  const int kMaxInt = std::numeric_limits<int>::max();
  EXPECT_EQ(TestUnit::FromKilo(kMaxInt).ToValue<int64_t>(),
            static_cast<int64_t>(kMaxInt) * 1000);
}

TEST(UnitBaseTest, ConvertsToAndFromDouble) {
  const int64_t kValue = 17017;
  const double kMilliDouble = kValue * 1e3;
  const double kValueDouble = kValue;
  const double kKiloDouble = kValue * 1e-3;

  EXPECT_EQ(TestUnit::FromValue(kValue).ToKilo<double>(), kKiloDouble);
  EXPECT_EQ(TestUnit::FromKilo(kKiloDouble).ToValue<int64_t>(), kValue);

  EXPECT_EQ(TestUnit::FromValue(kValue).ToValue<double>(), kValueDouble);
  EXPECT_EQ(TestUnit::FromValue(kValueDouble).ToValue<int64_t>(), kValue);

  EXPECT_NEAR(TestUnit::FromValue(kValue).ToMilli<double>(), kMilliDouble, 1);

  const double kPlusInfinity = std::numeric_limits<double>::infinity();
  const double kMinusInfinity = -kPlusInfinity;

  EXPECT_EQ(TestUnit::PlusInfinity().ToKilo<double>(), kPlusInfinity);
  EXPECT_EQ(TestUnit::MinusInfinity().ToKilo<double>(), kMinusInfinity);
  EXPECT_EQ(TestUnit::PlusInfinity().ToValue<double>(), kPlusInfinity);
  EXPECT_EQ(TestUnit::MinusInfinity().ToValue<double>(), kMinusInfinity);
  EXPECT_EQ(TestUnit::PlusInfinity().ToMilli<double>(), kPlusInfinity);
  EXPECT_EQ(TestUnit::MinusInfinity().ToMilli<double>(), kMinusInfinity);

  EXPECT_TRUE(TestUnit::FromKilo(kPlusInfinity).IsPlusInfinity());
  EXPECT_TRUE(TestUnit::FromKilo(kMinusInfinity).IsMinusInfinity());
  EXPECT_TRUE(TestUnit::FromValue(kPlusInfinity).IsPlusInfinity());
  EXPECT_TRUE(TestUnit::FromValue(kMinusInfinity).IsMinusInfinity());
}

TEST(UnitBaseTest, MathOperations) {
  const int64_t kValueA = 267;
  const int64_t kValueB = 450;
  const TestUnit delta_a = TestUnit::FromKilo(kValueA);
  const TestUnit delta_b = TestUnit::FromKilo(kValueB);
  EXPECT_EQ((delta_a + delta_b).ToKilo(), kValueA + kValueB);
  EXPECT_EQ((delta_a - delta_b).ToKilo(), kValueA - kValueB);

  const int32_t kInt32Value = 123;
  const double kFloatValue = 123.0;
  EXPECT_EQ((TestUnit::FromValue(kValueA) * kValueB).ToValue<int64_t>(),
            kValueA * kValueB);
  EXPECT_EQ((TestUnit::FromValue(kValueA) * kInt32Value).ToValue<int64_t>(),
            kValueA * kInt32Value);
  EXPECT_EQ((TestUnit::FromValue(kValueA) * kFloatValue).ToValue<int64_t>(),
            kValueA * kFloatValue);

  EXPECT_EQ((delta_b / 10).ToKilo(), kValueB / 10);
  EXPECT_EQ(delta_b / delta_a, static_cast<double>(kValueB) / kValueA);

  TestUnit mutable_delta = TestUnit::FromKilo(kValueA);
  mutable_delta += TestUnit::FromKilo(kValueB);
  EXPECT_EQ(mutable_delta, TestUnit::FromKilo(kValueA + kValueB));
  mutable_delta -= TestUnit::FromKilo(kValueB);
  EXPECT_EQ(mutable_delta, TestUnit::FromKilo(kValueA));
}

TEST(UnitBaseTest, InfinityOperations) {
  const int64_t kValue = 267;
  const TestUnit finite = TestUnit::FromKilo(kValue);
  EXPECT_TRUE((TestUnit::PlusInfinity() + finite).IsPlusInfinity());
  EXPECT_TRUE((TestUnit::PlusInfinity() - finite).IsPlusInfinity());
  EXPECT_TRUE((finite + TestUnit::PlusInfinity()).IsPlusInfinity());
  EXPECT_TRUE((finite - TestUnit::MinusInfinity()).IsPlusInfinity());

  EXPECT_TRUE((TestUnit::MinusInfinity() + finite).IsMinusInfinity());
  EXPECT_TRUE((TestUnit::MinusInfinity() - finite).IsMinusInfinity());
  EXPECT_TRUE((finite + TestUnit::MinusInfinity()).IsMinusInfinity());
  EXPECT_TRUE((finite - TestUnit::PlusInfinity()).IsMinusInfinity());
}
}  // namespace test
}  // namespace webrtc
<commit_msg>Fix errors C2238, C2248 and C2059 on MSVC bots.<commit_after>/*
 *  Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "rtc_base/units/unit_base.h"

#include "test/gtest.h"

namespace webrtc {
namespace {
class TestUnit final : public rtc_units_impl::RelativeUnit<TestUnit> {
 public:
  TestUnit() = delete;

  using UnitBase::FromValue;
  using UnitBase::ToValue;
  using UnitBase::ToValueOr;

  template <typename T>
  static constexpr TestUnit FromKilo(T kilo) {
    return FromFraction(1000, kilo);
  }
  template <typename T = int64_t>
  T ToKilo() const {
    return UnitBase::ToFraction<1000, T>();
  }
  constexpr int64_t ToKiloOr(int64_t fallback) const {
    return UnitBase::ToFractionOr<1000>(fallback);
  }
  template <typename T>
  constexpr T ToMilli() const {
    return UnitBase::ToMultiple<1000, T>();
  }

 private:
  friend class rtc_units_impl::UnitBase<TestUnit>;
  static constexpr bool one_sided = false;
  using RelativeUnit<TestUnit>::RelativeUnit;
};
constexpr TestUnit TestUnitAddKilo(TestUnit value, int add_kilo) {
  value += TestUnit::FromKilo(add_kilo);
  return value;
}
}  // namespace
namespace test {
TEST(UnitBaseTest, ConstExpr) {
  constexpr int64_t kValue = -12345;
  constexpr TestUnit kTestUnitZero = TestUnit::Zero();
  constexpr TestUnit kTestUnitPlusInf = TestUnit::PlusInfinity();
  constexpr TestUnit kTestUnitMinusInf = TestUnit::MinusInfinity();
  static_assert(kTestUnitZero.IsZero(), "");
  static_assert(kTestUnitPlusInf.IsPlusInfinity(), "");
  static_assert(kTestUnitMinusInf.IsMinusInfinity(), "");
  static_assert(kTestUnitPlusInf.ToKiloOr(-1) == -1, "");

  static_assert(kTestUnitPlusInf > kTestUnitZero, "");

  constexpr TestUnit kTestUnitKilo = TestUnit::FromKilo(kValue);
  constexpr TestUnit kTestUnitValue = TestUnit::FromValue(kValue);

  static_assert(kTestUnitKilo.ToKiloOr(0) == kValue, "");
  static_assert(kTestUnitValue.ToValueOr(0) == kValue, "");
  static_assert(TestUnitAddKilo(kTestUnitValue, 2).ToValue() == kValue + 2000,
                "");
}

TEST(UnitBaseTest, GetBackSameValues) {
  const int64_t kValue = 499;
  for (int sign = -1; sign <= 1; ++sign) {
    int64_t value = kValue * sign;
    EXPECT_EQ(TestUnit::FromKilo(value).ToKilo(), value);
    EXPECT_EQ(TestUnit::FromValue(value).ToValue<int64_t>(), value);
  }
  EXPECT_EQ(TestUnit::Zero().ToValue<int64_t>(), 0);
}

TEST(UnitBaseTest, GetDifferentPrefix) {
  const int64_t kValue = 3000000;
  EXPECT_EQ(TestUnit::FromValue(kValue).ToKilo(), kValue / 1000);
  EXPECT_EQ(TestUnit::FromKilo(kValue).ToValue<int64_t>(), kValue * 1000);
}

TEST(UnitBaseTest, IdentityChecks) {
  const int64_t kValue = 3000;
  EXPECT_TRUE(TestUnit::Zero().IsZero());
  EXPECT_FALSE(TestUnit::FromKilo(kValue).IsZero());

  EXPECT_TRUE(TestUnit::PlusInfinity().IsInfinite());
  EXPECT_TRUE(TestUnit::MinusInfinity().IsInfinite());
  EXPECT_FALSE(TestUnit::Zero().IsInfinite());
  EXPECT_FALSE(TestUnit::FromKilo(-kValue).IsInfinite());
  EXPECT_FALSE(TestUnit::FromKilo(kValue).IsInfinite());

  EXPECT_FALSE(TestUnit::PlusInfinity().IsFinite());
  EXPECT_FALSE(TestUnit::MinusInfinity().IsFinite());
  EXPECT_TRUE(TestUnit::FromKilo(-kValue).IsFinite());
  EXPECT_TRUE(TestUnit::FromKilo(kValue).IsFinite());
  EXPECT_TRUE(TestUnit::Zero().IsFinite());

  EXPECT_TRUE(TestUnit::PlusInfinity().IsPlusInfinity());
  EXPECT_FALSE(TestUnit::MinusInfinity().IsPlusInfinity());

  EXPECT_TRUE(TestUnit::MinusInfinity().IsMinusInfinity());
  EXPECT_FALSE(TestUnit::PlusInfinity().IsMinusInfinity());
}

TEST(UnitBaseTest, ComparisonOperators) {
  const int64_t kSmall = 450;
  const int64_t kLarge = 451;
  const TestUnit small = TestUnit::FromKilo(kSmall);
  const TestUnit large = TestUnit::FromKilo(kLarge);

  EXPECT_EQ(TestUnit::Zero(), TestUnit::FromKilo(0));
  EXPECT_EQ(TestUnit::PlusInfinity(), TestUnit::PlusInfinity());
  EXPECT_EQ(small, TestUnit::FromKilo(kSmall));
  EXPECT_LE(small, TestUnit::FromKilo(kSmall));
  EXPECT_GE(small, TestUnit::FromKilo(kSmall));
  EXPECT_NE(small, TestUnit::FromKilo(kLarge));
  EXPECT_LE(small, TestUnit::FromKilo(kLarge));
  EXPECT_LT(small, TestUnit::FromKilo(kLarge));
  EXPECT_GE(large, TestUnit::FromKilo(kSmall));
  EXPECT_GT(large, TestUnit::FromKilo(kSmall));
  EXPECT_LT(TestUnit::Zero(), small);
  EXPECT_GT(TestUnit::Zero(), TestUnit::FromKilo(-kSmall));
  EXPECT_GT(TestUnit::Zero(), TestUnit::FromKilo(-kSmall));

  EXPECT_GT(TestUnit::PlusInfinity(), large);
  EXPECT_LT(TestUnit::MinusInfinity(), TestUnit::Zero());
}

TEST(UnitBaseTest, Clamping) {
  const TestUnit upper = TestUnit::FromKilo(800);
  const TestUnit lower = TestUnit::FromKilo(100);
  const TestUnit under = TestUnit::FromKilo(100);
  const TestUnit inside = TestUnit::FromKilo(500);
  const TestUnit over = TestUnit::FromKilo(1000);
  EXPECT_EQ(under.Clamped(lower, upper), lower);
  EXPECT_EQ(inside.Clamped(lower, upper), inside);
  EXPECT_EQ(over.Clamped(lower, upper), upper);

  TestUnit mutable_delta = lower;
  mutable_delta.Clamp(lower, upper);
  EXPECT_EQ(mutable_delta, lower);
  mutable_delta = inside;
  mutable_delta.Clamp(lower, upper);
  EXPECT_EQ(mutable_delta, inside);
  mutable_delta = over;
  mutable_delta.Clamp(lower, upper);
  EXPECT_EQ(mutable_delta, upper);
}

TEST(UnitBaseTest, CanBeInititializedFromLargeInt) {
  const int kMaxInt = std::numeric_limits<int>::max();
  EXPECT_EQ(TestUnit::FromKilo(kMaxInt).ToValue<int64_t>(),
            static_cast<int64_t>(kMaxInt) * 1000);
}

TEST(UnitBaseTest, ConvertsToAndFromDouble) {
  const int64_t kValue = 17017;
  const double kMilliDouble = kValue * 1e3;
  const double kValueDouble = kValue;
  const double kKiloDouble = kValue * 1e-3;

  EXPECT_EQ(TestUnit::FromValue(kValue).ToKilo<double>(), kKiloDouble);
  EXPECT_EQ(TestUnit::FromKilo(kKiloDouble).ToValue<int64_t>(), kValue);

  EXPECT_EQ(TestUnit::FromValue(kValue).ToValue<double>(), kValueDouble);
  EXPECT_EQ(TestUnit::FromValue(kValueDouble).ToValue<int64_t>(), kValue);

  EXPECT_NEAR(TestUnit::FromValue(kValue).ToMilli<double>(), kMilliDouble, 1);

  const double kPlusInfinity = std::numeric_limits<double>::infinity();
  const double kMinusInfinity = -kPlusInfinity;

  EXPECT_EQ(TestUnit::PlusInfinity().ToKilo<double>(), kPlusInfinity);
  EXPECT_EQ(TestUnit::MinusInfinity().ToKilo<double>(), kMinusInfinity);
  EXPECT_EQ(TestUnit::PlusInfinity().ToValue<double>(), kPlusInfinity);
  EXPECT_EQ(TestUnit::MinusInfinity().ToValue<double>(), kMinusInfinity);
  EXPECT_EQ(TestUnit::PlusInfinity().ToMilli<double>(), kPlusInfinity);
  EXPECT_EQ(TestUnit::MinusInfinity().ToMilli<double>(), kMinusInfinity);

  EXPECT_TRUE(TestUnit::FromKilo(kPlusInfinity).IsPlusInfinity());
  EXPECT_TRUE(TestUnit::FromKilo(kMinusInfinity).IsMinusInfinity());
  EXPECT_TRUE(TestUnit::FromValue(kPlusInfinity).IsPlusInfinity());
  EXPECT_TRUE(TestUnit::FromValue(kMinusInfinity).IsMinusInfinity());
}

TEST(UnitBaseTest, MathOperations) {
  const int64_t kValueA = 267;
  const int64_t kValueB = 450;
  const TestUnit delta_a = TestUnit::FromKilo(kValueA);
  const TestUnit delta_b = TestUnit::FromKilo(kValueB);
  EXPECT_EQ((delta_a + delta_b).ToKilo(), kValueA + kValueB);
  EXPECT_EQ((delta_a - delta_b).ToKilo(), kValueA - kValueB);

  const int32_t kInt32Value = 123;
  const double kFloatValue = 123.0;
  EXPECT_EQ((TestUnit::FromValue(kValueA) * kValueB).ToValue<int64_t>(),
            kValueA * kValueB);
  EXPECT_EQ((TestUnit::FromValue(kValueA) * kInt32Value).ToValue<int64_t>(),
            kValueA * kInt32Value);
  EXPECT_EQ((TestUnit::FromValue(kValueA) * kFloatValue).ToValue<int64_t>(),
            kValueA * kFloatValue);

  EXPECT_EQ((delta_b / 10).ToKilo(), kValueB / 10);
  EXPECT_EQ(delta_b / delta_a, static_cast<double>(kValueB) / kValueA);

  TestUnit mutable_delta = TestUnit::FromKilo(kValueA);
  mutable_delta += TestUnit::FromKilo(kValueB);
  EXPECT_EQ(mutable_delta, TestUnit::FromKilo(kValueA + kValueB));
  mutable_delta -= TestUnit::FromKilo(kValueB);
  EXPECT_EQ(mutable_delta, TestUnit::FromKilo(kValueA));
}

TEST(UnitBaseTest, InfinityOperations) {
  const int64_t kValue = 267;
  const TestUnit finite = TestUnit::FromKilo(kValue);
  EXPECT_TRUE((TestUnit::PlusInfinity() + finite).IsPlusInfinity());
  EXPECT_TRUE((TestUnit::PlusInfinity() - finite).IsPlusInfinity());
  EXPECT_TRUE((finite + TestUnit::PlusInfinity()).IsPlusInfinity());
  EXPECT_TRUE((finite - TestUnit::MinusInfinity()).IsPlusInfinity());

  EXPECT_TRUE((TestUnit::MinusInfinity() + finite).IsMinusInfinity());
  EXPECT_TRUE((TestUnit::MinusInfinity() - finite).IsMinusInfinity());
  EXPECT_TRUE((finite + TestUnit::MinusInfinity()).IsMinusInfinity());
  EXPECT_TRUE((finite - TestUnit::PlusInfinity()).IsMinusInfinity());
}
}  // namespace test
}  // namespace webrtc
<|endoftext|>