<commit_before>/*
 * Copyright (c) 2014 - Franz Detro
 *
 * Some real world test program for motor control
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

#include "ev3dev.h"

#include <thread>
#include <chrono>
#include <iostream>
#include <fstream>

#ifndef NO_LINUX_HEADERS
#include <unistd.h>
#include <fcntl.h>
#include <linux/input.h>
#define KEY_RELEASE 0
#define KEY_PRESS   1
#define KEY_REPEAT  2
#endif

using namespace std;
using namespace ev3dev;

class control
{
public:
  control();
  ~control();

  void drive(int speed, int time=0);
  void turn(int direction);
  void stop();
  void reset();

  bool initialized() const;

  void terminate_on_key();
  void panic_if_touched();

  void remote_loop();
  void drive_autonomously();

  void terminate() { _terminate = true; }

protected:
  large_motor     _motor_left;
  large_motor     _motor_right;
  infrared_sensor _sensor_ir;
  touch_sensor    _sensor_touch;

  enum state
  {
    state_idle,
    state_driving,
    state_turning
  };

  state _state;
  bool  _terminate;
};

control::control() :
  _motor_left(2),
  _motor_right(3),
  _state(state_idle),
  _terminate(false)
{
}

control::~control()
{
  reset();
}

void control::drive(int speed, int time)
{
  if (time > 0)
  {
    _motor_left .set_run_mode(motor::run_mode_time);
    _motor_right.set_run_mode(motor::run_mode_time);

    _motor_left .set_time_setpoint(time);
    _motor_right.set_time_setpoint(time);
  }
  else
  {
    _motor_left .set_run_mode(motor::run_mode_forever);
    _motor_right.set_run_mode(motor::run_mode_forever);
  }

  _motor_left.set_duty_cycle_setpoint(-speed);

  _motor_right.set_duty_cycle_setpoint(-speed);

  _state = state_driving;

  _motor_left .run();
  _motor_right.run();

  if (time > 0)
  {
    while (_motor_left.running() || _motor_right.running())
      this_thread::sleep_for(chrono::milliseconds(10));

    _state = state_idle;
  }
}

void control::turn(int direction)
{
  if (_state != state_idle)
    stop();

  if (direction == 0)
    return;

  _motor_left.set_run_mode(motor::run_mode_position);
  //_motor_left.set_position_mode(motor::position_mode_relative);
  _motor_left.set_position(0);
  _motor_left .set_position_setpoint(direction);
  //_motor_left.set_regulation_mode(motor::mode_on);
  _motor_left.set_duty_cycle_setpoint(50);

  _motor_right.set_run_mode(motor::run_mode_position);
  //_motor_right.set_position_mode(motor::position_mode_relative);
  _motor_right.set_position(0);
  _motor_right.set_position_setpoint(-direction);
  //_motor_right.set_regulation_mode(motor::mode_on);
  _motor_right.set_duty_cycle_setpoint(50);

  _state = state_turning;

  _motor_left .run();
  _motor_right.run();

  while (_motor_left.running() || _motor_right.running())
    this_thread::sleep_for(chrono::milliseconds(10));

  _state = state_idle;
}

void control::stop()
{
  _motor_left .stop();
  _motor_right.stop();

  _state = state_idle;
}

void control::reset()
{
  _motor_left .reset();
  _motor_right.reset();

  _state = state_idle;
}

bool control::initialized() const
{
  return (_motor_left .connected() &&
          _motor_right.connected() &&
          _sensor_ir  .connected());
}

void control::terminate_on_key()
{
 #ifndef NO_LINUX_HEADERS
  thread t([&] () {
    int fd = open("/dev/input/by-path/platform-gpio-keys.0-event", O_RDONLY);
    if (fd  < 0)
    {
      cout << "Couldn't open platform-gpio-keys device!" << endl;
      return;
    }

    input_event ev;
    while (true)
    {
      size_t rb = read(fd, &ev, sizeof(ev));

      if (rb < sizeof(input_event))
        continue;

      if ((ev.type == EV_KEY) /*&& (ev.value == KEY_PRESS)*/)
      {
        terminate();
        return;
      }
    }
  });
  t.detach();
 #endif
}

void control::panic_if_touched()
{
  if (!_sensor_touch.connected())
  {
    cout << "no touch sensor found!" << endl;
    return;
  }

  thread t([&] () {
    while (!_terminate) {
      if (_sensor_touch.value())
      {
        terminate();
        reset();
        break;
      }
      this_thread::sleep_for(chrono::milliseconds(100));
    }
  });
  t.detach();
}

void control::remote_loop()
{
  remote_control r(_sensor_ir);

  if (!r.connected())
  {
    cout << "no infrared sensor found!" << endl;
    return;
  }

  const int speed = 70;
  const int ninety_degrees = 260;

  r.on_red_up = [&] (bool state)
  {
    if (state)
    {
      if (_state == state_idle)
        drive(speed);
    }
    else
      stop();
  };

  r.on_red_down = [&] (bool state)
  {
    if (state)
    {
      if (_state == state_idle)
        drive(-speed);
    }
    else
      stop();
  };

  r.on_blue_up = [&] (bool state)
  {
    if (state)
    {
      if (_state == state_idle)
        turn(-ninety_degrees);
    }
  };

  r.on_blue_down = [&] (bool state)
  {
    if (state)
    {
      if (_state == state_idle)
        turn(ninety_degrees);
    }
  };

  r.on_beacon = [&] (bool state)
  {
    if (state)
      terminate();
  };

  while (!_terminate)
  {
    if (!r.process())
    {
      this_thread::sleep_for(chrono::milliseconds(10));
    }
  }

  reset();
}

void control::drive_autonomously()
{
  if (!_sensor_ir.connected())
  {
    cout << "no infrared sensor found!" << endl;
    return;
  }

  _sensor_ir.set_mode(infrared_sensor::mode_proximity);

  while (!_terminate)
  {
    int distance = _sensor_ir.value();
    if (distance <= 0)
    {
      // panic
      terminate();
      reset();
      break;
    }
    else if (distance >= 20)
    {
      if (_state != state_driving)
        drive(75);
      this_thread::sleep_for(chrono::milliseconds(10));
    }
    else
    {
      stop();

      int direction = 100;
      int start_distance = distance;

      while (distance <= 40)
      {
        turn(direction);

        distance = _sensor_ir.value();
        if (distance < start_distance)
        {
          if (direction < 0)
          {
            drive(-70, 1000);
          }
          else
          {
            direction = -200;
          }
        }
      }
    }
  }
}

int main()
{
  control c;

  if (c.initialized())
  {
    c.terminate_on_key(); // we terminate if a button is pressed
    c.panic_if_touched(); // we panic if the touch sensor is triggered

  // change mode to 1 to get IR remote mode
    int mode = 2;
    if (mode == 1)
    {
      cout << "ensure that channel 1 is selected on your remote control." << endl << endl
           << "upper red button  - forward" << endl
           << "lower red button  - backward" << endl
           << "upper blue button - left" << endl
           << "lower blue button - right" << endl
           << "middle button     - exit" << endl << endl;

      c.remote_loop();
    }
    else if (mode == 2)
    {
      cout << "touch the sensor or press a button to stop." << endl << endl;

      c.drive_autonomously();
    }
  }
  else
  {
    cout << "you need to connect an infrared sensor and large motors to ports B and C!" << endl;
    return 1;
  }

  return 0;
}
<commit_msg>fixed an exception issue in cpp drive-test.cpp<commit_after>/*
 * Copyright (c) 2014 - Franz Detro
 *
 * Some real world test program for motor control
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

#include "ev3dev.h"

#include <thread>
#include <chrono>
#include <iostream>
#include <fstream>

#ifndef NO_LINUX_HEADERS
#include <unistd.h>
#include <fcntl.h>
#include <linux/input.h>
#define KEY_RELEASE 0
#define KEY_PRESS   1
#define KEY_REPEAT  2
#endif

using namespace std;
using namespace ev3dev;

class control
{
public:
  control();
  ~control();

  void drive(int speed, int time=0);
  void turn(int direction);
  void stop();
  void reset();

  bool initialized() const;

  void terminate_on_key();
  void panic_if_touched();

  void remote_loop();
  void drive_autonomously();

  void terminate() { _terminate = true; }

protected:
  large_motor     _motor_left;
  large_motor     _motor_right;
  infrared_sensor _sensor_ir;
  touch_sensor    _sensor_touch;

  enum state
  {
    state_idle,
    state_driving,
    state_turning
  };

  state _state;
  bool  _terminate;
};

control::control() :
  _motor_left(2),
  _motor_right(3),
  _state(state_idle),
  _terminate(false)
{
}

control::~control()
{
  reset();
}

void control::drive(int speed, int time)
{
  if (time > 0)
  {
    _motor_left .set_run_mode(motor::run_mode_time);
    _motor_right.set_run_mode(motor::run_mode_time);

    _motor_left .set_time_setpoint(time);
    _motor_right.set_time_setpoint(time);
  }
  else
  {
    _motor_left .set_run_mode(motor::run_mode_forever);
    _motor_right.set_run_mode(motor::run_mode_forever);
  }

  _motor_left.set_duty_cycle_setpoint(-speed);

  _motor_right.set_duty_cycle_setpoint(-speed);

  _state = state_driving;

  _motor_left .run();
  _motor_right.run();

  if (time > 0)
  {
    while (_motor_left.running() || _motor_right.running())
      this_thread::sleep_for(chrono::milliseconds(10));

    _state = state_idle;
  }
}

void control::turn(int direction)
{
  if (_state != state_idle)
    stop();

  if (direction == 0)
    return;

  _motor_left.set_run_mode(motor::run_mode_position);
  //_motor_left.set_position_mode(motor::position_mode_relative);
  _motor_left.set_position(0);
  _motor_left .set_position_setpoint(direction);
  //_motor_left.set_regulation_mode(motor::mode_on);
  _motor_left.set_duty_cycle_setpoint(50);

  _motor_right.set_run_mode(motor::run_mode_position);
  //_motor_right.set_position_mode(motor::position_mode_relative);
  _motor_right.set_position(0);
  _motor_right.set_position_setpoint(-direction);
  //_motor_right.set_regulation_mode(motor::mode_on);
  _motor_right.set_duty_cycle_setpoint(50);

  _state = state_turning;

  _motor_left .run();
  _motor_right.run();

  while (_motor_left.running() || _motor_right.running())
    this_thread::sleep_for(chrono::milliseconds(10));

  _state = state_idle;
}

void control::stop()
{
  _motor_left .stop();
  _motor_right.stop();

  _state = state_idle;
}

void control::reset()
{
  if (_motor_left.connected())
    _motor_left .reset();

  if (_motor_right.connected())
    _motor_right.reset();

  _state = state_idle;
}

bool control::initialized() const
{
  return (_motor_left .connected() &&
          _motor_right.connected() &&
          _sensor_ir  .connected());
}

void control::terminate_on_key()
{
 #ifndef NO_LINUX_HEADERS
  thread t([&] () {
    int fd = open("/dev/input/by-path/platform-gpio-keys.0-event", O_RDONLY);
    if (fd  < 0)
    {
      cout << "Couldn't open platform-gpio-keys device!" << endl;
      return;
    }

    input_event ev;
    while (true)
    {
      size_t rb = read(fd, &ev, sizeof(ev));

      if (rb < sizeof(input_event))
        continue;

      if ((ev.type == EV_KEY) /*&& (ev.value == KEY_PRESS)*/)
      {
        terminate();
        return;
      }
    }
  });
  t.detach();
 #endif
}

void control::panic_if_touched()
{
  if (!_sensor_touch.connected())
  {
    cout << "no touch sensor found!" << endl;
    return;
  }

  thread t([&] () {
    while (!_terminate) {
      if (_sensor_touch.value())
      {
        terminate();
        reset();
        break;
      }
      this_thread::sleep_for(chrono::milliseconds(100));
    }
  });
  t.detach();
}

void control::remote_loop()
{
  remote_control r(_sensor_ir);

  if (!r.connected())
  {
    cout << "no infrared sensor found!" << endl;
    return;
  }

  const int speed = 70;
  const int ninety_degrees = 260;

  r.on_red_up = [&] (bool state)
  {
    if (state)
    {
      if (_state == state_idle)
        drive(speed);
    }
    else
      stop();
  };

  r.on_red_down = [&] (bool state)
  {
    if (state)
    {
      if (_state == state_idle)
        drive(-speed);
    }
    else
      stop();
  };

  r.on_blue_up = [&] (bool state)
  {
    if (state)
    {
      if (_state == state_idle)
        turn(-ninety_degrees);
    }
  };

  r.on_blue_down = [&] (bool state)
  {
    if (state)
    {
      if (_state == state_idle)
        turn(ninety_degrees);
    }
  };

  r.on_beacon = [&] (bool state)
  {
    if (state)
      terminate();
  };

  while (!_terminate)
  {
    if (!r.process())
    {
      this_thread::sleep_for(chrono::milliseconds(10));
    }
  }

  reset();
}

void control::drive_autonomously()
{
  if (!_sensor_ir.connected())
  {
    cout << "no infrared sensor found!" << endl;
    return;
  }

  _sensor_ir.set_mode(infrared_sensor::mode_proximity);

  while (!_terminate)
  {
    int distance = _sensor_ir.value();
    if (distance <= 0)
    {
      // panic
      terminate();
      reset();
      break;
    }
    else if (distance >= 20)
    {
      if (_state != state_driving)
        drive(75);
      this_thread::sleep_for(chrono::milliseconds(10));
    }
    else
    {
      stop();

      int direction = 100;
      int start_distance = distance;

      while (distance <= 40)
      {
        turn(direction);

        distance = _sensor_ir.value();
        if (distance < start_distance)
        {
          if (direction < 0)
          {
            drive(-70, 1000);
          }
          else
          {
            direction = -200;
          }
        }
      }
    }
  }
}

int main()
{
  control c;

  if (c.initialized())
  {
    c.terminate_on_key(); // we terminate if a button is pressed
    c.panic_if_touched(); // we panic if the touch sensor is triggered

  // change mode to 1 to get IR remote mode
    int mode = 2;
    if (mode == 1)
    {
      cout << "ensure that channel 1 is selected on your remote control." << endl << endl
           << "upper red button  - forward" << endl
           << "lower red button  - backward" << endl
           << "upper blue button - left" << endl
           << "lower blue button - right" << endl
           << "middle button     - exit" << endl << endl;

      c.remote_loop();
    }
    else if (mode == 2)
    {
      cout << "touch the sensor or press a button to stop." << endl << endl;

      c.drive_autonomously();
    }
  }
  else
  {
    cout << "you need to connect an infrared sensor and large motors to ports B and C!" << endl;
    return 1;
  }

  return 0;
}
<|endoftext|>


<commit_before>#include <cstdio> // sscanf
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <vector>

extern "C" {
	#include "../../src/ASSInspector.h"
}

struct DialogLine {
	int time;
	std::string line;
};

class ScriptWrapper {
		std::string headerBuffer;
		std::vector<DialogLine> lineBuffers;

	public:
		ScriptWrapper( void );
		void appendHeader( std::string line );
		std::string getHeader( void );
		std::vector<DialogLine> getLines( void );
		void appendDialogue( std::string line, int time );
};

ScriptWrapper::ScriptWrapper( void ) :
headerBuffer( "[Script Info]\n" ) {

}

void ScriptWrapper::appendHeader( std::string line ) {
	headerBuffer += line + "\n";
}

std::string ScriptWrapper::getHeader( void ) {
	return headerBuffer;
}

void ScriptWrapper::appendDialogue( std::string line, int time ) {
	lineBuffers.push_back( {time, line + '\n'} );
}

std::vector<DialogLine> ScriptWrapper::getLines( void ) {
	return lineBuffers;
}

// this should definitely all be one function it is so good
int main( int argc, char **argv ) {
	int width = 0, height = 0;

	if ( 2 > argc ) {
		std::cout << "Usage: " << argv[0] << " infile.ass" << std::endl;
		return 1;
	}

	std::fstream inAss( argv[1], std::ifstream::in );
	if ( inAss.fail( ) ) {
		std::cout << "Failed to open " << argv[1] << std::endl;
	}

	std::string line;
	std::getline( inAss, line );
	if ( "[Script Info]" != line ) {
		// BOM?
		if ( "[Script Info]" != line.substr( 3 ) ) {
			std::cout << "The input file, " << argv[1] << ", does not appear to be valid ASS." << std::endl;
			inAss.close( );
			return 1;
		}
	}

	ScriptWrapper wrapper;

	// This is pretty terrible.
	std::getline( inAss, line );
	while ( inAss.good( ) ) {
		if ( '[' == line[0] ) {
			break;
		}
		switch ( line[0] ) {
			case 'S':
				if ( 0 == line.compare( 0, 22, "ScaledBorderAndShadow:" ) ) {
					wrapper.appendHeader( line );
				} else if ( 0 == line.compare( 0, 11, "ScriptType:" ) ) {
					wrapper.appendHeader( line );
				}
				break;
			case 'P':
				if ( 0 == line.compare( 0, 9, "PlayResX:" ) ) {
					std::stringstream converter( line.substr( 9 ) );
					converter >> width;
					wrapper.appendHeader( line );
				} else if ( 0 == line.compare( 0, 9, "PlayResY:" ) ) {
					std::stringstream converter( line.substr( 9 ) );
					converter >> height;
					wrapper.appendHeader( line );
				}
				break;
			case 'W':
				if ( 0 == line.compare( 0, 10, "WrapStyle:" ) ) {
					wrapper.appendHeader( line );
				}
				break;
		}
		std::getline( inAss, line );
	}

	wrapper.appendHeader( "[V4+ Styles]" );

	std::getline( inAss, line );
	while ( inAss.good( ) ) {
		switch ( line[0] ) {
			case 'S':
				if ( 0 == line.compare( 0, 6, "Style:" ) ) {
					wrapper.appendHeader( line );
				}
				break;
			case 'D':
				if ( 0 == line.compare( 0, 9, "Dialogue:" ) ) {
					int hours = 0, minutes = 0, seconds = 0, centiseconds = 0;
					sscanf( line.substr( 9 ).c_str( ), "%*d,%u:%u:%u.%u", &hours, &minutes, &seconds, &centiseconds );
					int milliseconds = 3600000*hours + 60000*minutes + 1000*seconds + 10*centiseconds;
					wrapper.appendDialogue( line, milliseconds );
				}
				break;
		}
		std::getline( inAss, line );
	}

	inAss.close( );

	wrapper.appendHeader( "[Events]" );

	ASSI_State *state = assi_init( width, height, NULL, NULL );

	std::string header = wrapper.getHeader( );
	assi_setHeader( state, header.c_str( ), header.length( ) );

	for ( const auto &pair : wrapper.getLines( ) ) {
		assi_setScript( state, pair.line.c_str( ), pair.line.length( ) );
		ASSI_Rect rect{ 0, 0, 0, 0 };
		assi_calculateBounds( state, &rect, &pair.time, 1 );
		if ( rect.w > 0 && rect.h > 0 ) {
			std::cout << pair.time << " -> " << pair.line;
			std::cout << "x: " << rect.x << " y: " << rect.y << " w: " << rect.w << " h: " << rect.h << std::endl;
		}
	}

	assi_cleanup( state );
	return 0;
}
<commit_msg>C++ example: read last line even with no final newline.<commit_after>#include <cstdio> // sscanf
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <vector>

extern "C" {
	#include "../../src/ASSInspector.h"
}

struct DialogLine {
	int time;
	std::string line;
};

class ScriptWrapper {
		std::string headerBuffer;
		std::vector<DialogLine> lineBuffers;

	public:
		ScriptWrapper( void );
		void appendHeader( std::string line );
		std::string getHeader( void );
		std::vector<DialogLine> getLines( void );
		void appendDialogue( std::string line, int time );
};

ScriptWrapper::ScriptWrapper( void ) :
headerBuffer( "[Script Info]\n" ) {

}

void ScriptWrapper::appendHeader( std::string line ) {
	headerBuffer += line + "\n";
}

std::string ScriptWrapper::getHeader( void ) {
	return headerBuffer;
}

void ScriptWrapper::appendDialogue( std::string line, int time ) {
	lineBuffers.push_back( {time, line + '\n'} );
}

std::vector<DialogLine> ScriptWrapper::getLines( void ) {
	return lineBuffers;
}

// this should definitely all be one function it is so good
int main( int argc, char **argv ) {
	int width = 0, height = 0;

	if ( 2 > argc ) {
		std::cout << "Usage: " << argv[0] << " infile.ass" << std::endl;
		return 1;
	}

	std::fstream inAss( argv[1], std::ifstream::in );
	if ( inAss.fail( ) ) {
		std::cout << "Failed to open " << argv[1] << std::endl;
	}

	std::string line;
	std::getline( inAss, line );
	if ( "[Script Info]" != line ) {
		// BOM?
		if ( "[Script Info]" != line.substr( 3 ) ) {
			std::cout << "The input file, " << argv[1] << ", does not appear to be valid ASS." << std::endl;
			inAss.close( );
			return 1;
		}
	}

	ScriptWrapper wrapper;

	// This is pretty terrible.
	while ( true ) {
		std::getline( inAss, line );
		if ( '[' == line[0] ) {
			break;
		}
		switch ( line[0] ) {
			case 'S':
				if ( 0 == line.compare( 0, 22, "ScaledBorderAndShadow:" ) ) {
					wrapper.appendHeader( line );
				} else if ( 0 == line.compare( 0, 11, "ScriptType:" ) ) {
					wrapper.appendHeader( line );
				}
				break;
			case 'P':
				if ( 0 == line.compare( 0, 9, "PlayResX:" ) ) {
					std::stringstream converter( line.substr( 9 ) );
					converter >> width;
					wrapper.appendHeader( line );
				} else if ( 0 == line.compare( 0, 9, "PlayResY:" ) ) {
					std::stringstream converter( line.substr( 9 ) );
					converter >> height;
					wrapper.appendHeader( line );
				}
				break;
			case 'W':
				if ( 0 == line.compare( 0, 10, "WrapStyle:" ) ) {
					wrapper.appendHeader( line );
				}
				break;
		}
		if ( !inAss.good( ) ) {
			break;
		}
	}

	wrapper.appendHeader( "[V4+ Styles]" );

	while ( true ) {
		std::getline( inAss, line );
		switch ( line[0] ) {
			case 'S':
				if ( 0 == line.compare( 0, 6, "Style:" ) ) {
					wrapper.appendHeader( line );
				}
				break;
			case 'D':
				if ( 0 == line.compare( 0, 9, "Dialogue:" ) ) {
					int hours = 0, minutes = 0, seconds = 0, centiseconds = 0;
					sscanf( line.substr( 9 ).c_str( ), "%*d,%u:%u:%u.%u", &hours, &minutes, &seconds, &centiseconds );
					int milliseconds = 3600000*hours + 60000*minutes + 1000*seconds + 10*centiseconds;
					wrapper.appendDialogue( line, milliseconds );
				}
				break;
		}
		if ( !inAss.good( ) ) {
			break;
		}
	}

	inAss.close( );

	wrapper.appendHeader( "[Events]" );

	ASSI_State *state = assi_init( width, height, NULL, NULL );

	std::string header = wrapper.getHeader( );
	assi_setHeader( state, header.c_str( ), header.length( ) );

	for ( const auto &pair : wrapper.getLines( ) ) {
		assi_setScript( state, pair.line.c_str( ), pair.line.length( ) );
		ASSI_Rect rect{ 0, 0, 0, 0 };
		assi_calculateBounds( state, &rect, &pair.time, 1 );
		if ( rect.w > 0 && rect.h > 0 ) {
			std::cout << pair.time << " -> " << pair.line;
			std::cout << "x: " << rect.x << " y: " << rect.y << " w: " << rect.w << " h: " << rect.h << std::endl;
		}
	}

	assi_cleanup( state );
	return 0;
}
<|endoftext|>


<commit_before>/*
The MIT License (MIT)

Copyright (c) 2015 Norman Wilson - Volcano Consultancy Ltd

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <L3/disruptor/disruptor.h>

#include <thread>
#include <iostream>

//
// Lets say we're going to use size_t as our message.
//
using Msg = size_t;
//
// Declare alias for a disruptor for this type of message and a ring
// buffer of size 2^19 = 512K messages.
//
using D = L3::Disruptor<Msg, 19>;
//
// Alias Put and Get types using defaults.
//
using Get = D::Get<>;
using Put = D::Put<>;
//
// We've now created a very basic disruptor with a single producer and
// consumer. We can use it like this...
//
int
main()
{
    //
    // To do a put we make an instance of the Put class we've just
    // defined.
    //
    {
        //
        // Creating a put claims a slot in the ring.
        //
        Put p;
        //
        // To write a value use assignment.
        //
        p = 1;
        //
        // When p goes out of scope the put is commited and published
        // to any consumers. It's important to note that once you have
        // started a put it will always be commited. RAII takes care
        // of that.
        //
    }
    //
    // Since unnamed temporaries have the lifetime of the statement
    // they are created in we can shorten the above to:
    //
    Put() = 2;
    //
    // To get we do something similar but gets are batched so we need
    // to read in a loop.
    //
    for(Msg m: Get())
    {
        std::cout << "got: " << m << std::endl;
    }
    //
    // We can send messages between two threads like so.
    //
    // 10 Million messages takes less than a second on my old macbook
    // pro.
    //
    const size_t loops = 10000000;
    //
    // Producer just writes sequencial integers.
    //
    std::thread producer([]{ for(size_t i = 1; i < loops; ++i) Put() = i; });
    //
    // Consumer checks that these are read in the same order.
    //
    Msg previous = 0;
    //
    // Should get the same number of messages we put.
    //
    for(size_t i = 1; i < loops;)
    {
        for(Msg m: Get())
        {
            if(m != previous + 1)
            {
                //
                // Will abort since producer not joined.
                //
                return -1;
            }
            previous = m;
            ++i;
        }
    }
    producer.join();
    return 0;
}
<commit_msg>Improve comments.<commit_after>/*
The MIT License (MIT)

Copyright (c) 2015 Norman Wilson - Volcano Consultancy Ltd

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <L3/disruptor/disruptor.h>

#include <thread>
#include <iostream>

//
// Lets say we're going to use size_t as our message.
//
using Msg = size_t;
//
// Declare alias for a disruptor for this type of message and a ring
// buffer of size 2^19 = 512K messages.
//
using D = L3::Disruptor<Msg, 19>;
//
// Alias Put and Get types using defaults.
//
using Get = D::Get<>;
using Put = D::Put<>;
//
// We've now created a very basic disruptor with a single producer and
// consumer. We can use it like this...
//
int
main()
{
    //
    // To do a put we make an instance of the Put class we've just
    // defined.
    //
    {
        //
        // Creating a put claims a slot in the ring.
        //
        Put p;
        //
        // To write a value use assignment.
        //
        p = 1;
        //
        // When p goes out of scope the put is commited and published
        // to any consumers. It's important to note that once you have
        // started a put it will always be commited. RAII takes care
        // of that.
        //
    }
    //
    // Since unnamed temporaries have the lifetime of the statement
    // they are created in we can shorten the above to:
    //
    Put() = 2;
    //
    // To get we do something similar but gets are batched so we need
    // to read in a loop.
    //
    for(Msg m: Get())
    {
        std::cout << "got: " << m << std::endl;
    }
    //
    // Get is committed when the temporary goes out of scope.
    //

    //
    // We can send messages between two threads like so.
    //
    // 10 Million messages takes less than a second on my old macbook
    // pro.
    //
    const size_t loops = 10000000;
    //
    // Producer just writes sequencial integers.
    //
    std::thread producer([]{ for(size_t i = 1; i < loops; ++i) Put() = i; });
    //
    // Consumer checks that these are read in the same order.
    //
    Msg previous = 0;
    //
    // Should get the same number of messages we put.
    //
    for(size_t i = 1; i < loops;)
    {
        for(Msg m: Get())
        {
            if(m != previous + 1)
            {
                //
                // Oops - something went wrong. Return here will abort
                // since producer not joined.
                //
                return -1;
            }
            previous = m;
            ++i;
        }
    }
    producer.join();
    return 0;
}
<|endoftext|>


<commit_before>// -----------------------------------------------------------------------------
//
// BSD 2-Clause License
//
// Copyright (c) 2017, sarah authors
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice, this
//   list of conditions and the following disclaimer.
//
// * 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 HOLDER 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 <sarah/matrix_creator.h>

namespace sarah
{

  namespace MatrixCreator
  {

    template<int dim, int spacedim = dim, typename ValueType = double>
    void
    create_mass_matrix (const dealii::FiniteElement<dim,spacedim> &finite_element,
			const dealii::DoFHandler<dim,spacedim>    &dof_handler,
			const dealii::Quadrature<dim>             &quadrature,
			dealii::PETScWrappers::MPI::SparseMatrix  &matrix,
			dealii::ConstraintMatrix                  &constraints,
			MPI_Comm                                  &mpi_communicator)
    {
      dealii::FEValues<dim> fe_values (finite_element, quadrature,
				       dealii::update_values            |
				       dealii::update_quadrature_points |
				       dealii::update_JxW_values);

      const unsigned int dofs_per_cell = finite_element.dofs_per_cell;
      const unsigned int n_q_points    = quadrature.size ();

      dealii::FullMatrix<double> cell_matrix (dofs_per_cell, dofs_per_cell);
      std::vector<dealii::types::global_dof_index> local_dof_indices (dofs_per_cell);

      typename dealii::DoFHandler<dim>::active_cell_iterator
	cell = dof_handler.begin_active (),
	endc = dof_handler.end ();

      for (; cell!=endc; ++cell)
	if (cell->subdomain_id () == dealii::Utilities::MPI::this_mpi_process (mpi_communicator))
	  {
	    cell_matrix = 0;
	    fe_values.reinit (cell);

	    for (unsigned int q_point=0; q_point<n_q_points; ++q_point)
	      for (unsigned int j=0; j<dofs_per_cell; ++j)
		for (unsigned int i=0; i<dofs_per_cell; ++i)
		  {
		    // Local stiffness (mass) matrix.
		    cell_matrix (i,j) +=
		      fe_values.shape_value (i,q_point) *
		      fe_values.shape_value (j,q_point) *
		      fe_values.JxW (q_point);
		  }

	    cell->get_dof_indices (local_dof_indices);

	    constraints.distribute_local_to_global (cell_matrix,
						    local_dof_indices,
						    matrix);
	  } // cell!=endc

      matrix.compress (dealii::VectorOperation::add);
    }

  } // namepsace MatrixCreator

} // namepsace sarah

template void
sarah::MatrixCreator::create_mass_matrix<3, 3, double> (const dealii::FiniteElement<3,3>         &,
							const dealii::DoFHandler<3,3>            &,
							const dealii::Quadrature<3>              &,
							dealii::PETScWrappers::MPI::SparseMatrix &,
							dealii::ConstraintMatrix                 &,
							MPI_Comm                                 &);

template void
sarah::MatrixCreator::create_mass_matrix<2, 2, double> (const dealii::FiniteElement<2,2>         &,
							const dealii::DoFHandler<2,2>            &,
							const dealii::Quadrature<2>              &,
							dealii::PETScWrappers::MPI::SparseMatrix &,
							dealii::ConstraintMatrix                 &,
							MPI_Comm                                 &);
<commit_msg>Use ValueType in MatrixCreator.<commit_after>// -----------------------------------------------------------------------------
//
// BSD 2-Clause License
//
// Copyright (c) 2017, sarah authors
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice, this
//   list of conditions and the following disclaimer.
//
// * 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 HOLDER 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 <sarah/matrix_creator.h>

namespace sarah
{

  namespace MatrixCreator
  {

    template<int dim, int spacedim = dim, typename ValueType = double>
    void
    create_mass_matrix (const dealii::FiniteElement<dim,spacedim> &finite_element,
			const dealii::DoFHandler<dim,spacedim>    &dof_handler,
			const dealii::Quadrature<dim>             &quadrature,
			dealii::PETScWrappers::MPI::SparseMatrix  &matrix,
			dealii::ConstraintMatrix                  &constraints,
			MPI_Comm                                  &mpi_communicator)
    {
      dealii::FEValues<dim> fe_values (finite_element, quadrature,
				       dealii::update_values            |
				       dealii::update_quadrature_points |
				       dealii::update_JxW_values);

      const unsigned int dofs_per_cell = finite_element.dofs_per_cell;
      const unsigned int n_q_points    = quadrature.size ();

      dealii::FullMatrix<ValueType> cell_matrix (dofs_per_cell, dofs_per_cell);
      std::vector<dealii::types::global_dof_index> local_dof_indices (dofs_per_cell);

      typename dealii::DoFHandler<dim>::active_cell_iterator
	cell = dof_handler.begin_active (),
	endc = dof_handler.end ();

      for (; cell!=endc; ++cell)
	if (cell->subdomain_id () == dealii::Utilities::MPI::this_mpi_process (mpi_communicator))
	  {
	    cell_matrix = 0;
	    fe_values.reinit (cell);

	    for (unsigned int q_point=0; q_point<n_q_points; ++q_point)
	      for (unsigned int j=0; j<dofs_per_cell; ++j)
		for (unsigned int i=0; i<dofs_per_cell; ++i)
		  {
		    // Local stiffness (mass) matrix.
		    cell_matrix (i,j) +=
		      fe_values.shape_value (i,q_point) *
		      fe_values.shape_value (j,q_point) *
		      fe_values.JxW (q_point);
		  }

	    cell->get_dof_indices (local_dof_indices);

	    constraints.distribute_local_to_global (cell_matrix,
						    local_dof_indices,
						    matrix);
	  } // cell!=endc

      matrix.compress (dealii::VectorOperation::add);
    }

  } // namepsace MatrixCreator

} // namepsace sarah

template void
sarah::MatrixCreator::create_mass_matrix<3, 3, double> (const dealii::FiniteElement<3,3>         &,
							const dealii::DoFHandler<3,3>            &,
							const dealii::Quadrature<3>              &,
							dealii::PETScWrappers::MPI::SparseMatrix &,
							dealii::ConstraintMatrix                 &,
							MPI_Comm                                 &);

template void
sarah::MatrixCreator::create_mass_matrix<2, 2, double> (const dealii::FiniteElement<2,2>         &,
							const dealii::DoFHandler<2,2>            &,
							const dealii::Quadrature<2>              &,
							dealii::PETScWrappers::MPI::SparseMatrix &,
							dealii::ConstraintMatrix                 &,
							MPI_Comm                                 &);
<|endoftext|>


<commit_before>/*
Copyright libCellML Contributors

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 "utilities.h"

#include "libcellml/importsource.h"
#include "libcellml/model.h"
#include "libcellml/units.h"

#include <algorithm>
#include <cassert>
#include <cmath>
#include <map>
#include <stdexcept>
#include <string>
#include <vector>

namespace libcellml {

/**
 * @brief Map Prefix to their string forms.
 *
 * An internal map used to convert a Prefix into its string form.
 */
static const std::map<Prefix, const std::string> prefixToString = {
    {Prefix::ATTO, "atto"},
    {Prefix::CENTI, "centi"},
    {Prefix::DECA, "deca"},
    {Prefix::DECI, "deci"},
    {Prefix::EXA, "exa"},
    {Prefix::FEMTO, "femto"},
    {Prefix::GIGA, "giga"},
    {Prefix::HECTO, "hecto"},
    {Prefix::KILO, "kilo"},
    {Prefix::MEGA, "mega"},
    {Prefix::MICRO, "micro"},
    {Prefix::MILLI, "milli"},
    {Prefix::NANO, "nano"},
    {Prefix::PETA, "peta"},
    {Prefix::PICO, "pico"},
    {Prefix::TERA, "tera"},
    {Prefix::YOCTO, "yocto"},
    {Prefix::YOTTA, "yotta"},
    {Prefix::ZEPTO, "zepto"},
    {Prefix::ZETTA, "zetta"}};

/**
 * @brief Map StandardUnit to their string forms.
 *
 * An internal map used to convert a standard unit into its string form.
 */
static const std::map<Units::StandardUnit, const std::string> standardUnitToString = {
    {Units::StandardUnit::AMPERE, "ampere"},
    {Units::StandardUnit::BECQUEREL, "becquerel"},
    {Units::StandardUnit::CANDELA, "candela"},
    {Units::StandardUnit::COULOMB, "coulomb"},
    {Units::StandardUnit::DIMENSIONLESS, "dimensionless"},
    {Units::StandardUnit::FARAD, "farad"},
    {Units::StandardUnit::GRAM, "gram"},
    {Units::StandardUnit::GRAY, "gray"},
    {Units::StandardUnit::HENRY, "henry"},
    {Units::StandardUnit::HERTZ, "hertz"},
    {Units::StandardUnit::JOULE, "joule"},
    {Units::StandardUnit::KATAL, "katal"},
    {Units::StandardUnit::KELVIN, "kelvin"},
    {Units::StandardUnit::KILOGRAM, "kilogram"},
    {Units::StandardUnit::LITRE, "litre"},
    {Units::StandardUnit::LUMEN, "lumen"},
    {Units::StandardUnit::LUX, "lux"},
    {Units::StandardUnit::METRE, "metre"},
    {Units::StandardUnit::MOLE, "mole"},
    {Units::StandardUnit::NEWTON, "newton"},
    {Units::StandardUnit::OHM, "ohm"},
    {Units::StandardUnit::PASCAL, "pascal"},
    {Units::StandardUnit::RADIAN, "radian"},
    {Units::StandardUnit::SECOND, "second"},
    {Units::StandardUnit::SIEMENS, "siemens"},
    {Units::StandardUnit::SIEVERT, "sievert"},
    {Units::StandardUnit::STERADIAN, "steradian"},
    {Units::StandardUnit::TESLA, "tesla"},
    {Units::StandardUnit::VOLT, "volt"},
    {Units::StandardUnit::WATT, "watt"},
    {Units::StandardUnit::WEBER, "weber"}};

/**
 * @brief The Unit struct.
 *
 * An internal structure to capture a unit definition.  The
 * prefix can be expressed using either an integer or an enum.
 * The enum structure member is given preference if both are set.
 */
struct Unit
{
    std::string mReference; /**< Reference to the units for the unit.*/
    std::string mPrefix; /**< String expression of the prefix for the unit.*/
    std::string mExponent; /**< Exponent for the unit.*/
    std::string mMultiplier; /**< Multiplier for the unit.*/
    std::string mId; /**< Id for the unit.*/
};

/**
 * @brief The Units::UnitsImpl struct.
 *
 * The private implementation for the Units class.
 */
struct Units::UnitsImpl
{
    std::vector<Unit> mUnits; /**< A vector of unit defined for this Units.*/

    std::vector<Unit>::iterator findUnit(const std::string &reference);
};

std::vector<Unit>::iterator Units::UnitsImpl::findUnit(const std::string &reference)
{
    return std::find_if(mUnits.begin(), mUnits.end(),
                        [=](const Unit &u) -> bool { return u.mReference == reference; });
}

/*

//TDOD: This code is progress towards checking that units are compatible.
using UnitMultiplierMap = std::map<std::string, double>;

    UnitMultiplierMap unitMap = {};
    for (const auto &baseUnits : baseUnitsList) {
        unitMap[baseUnits] = 0.0;
    }
bool updateUnitMultipliers(UnitMultiplierMap &unitMultiplierMap,
                           double &multiplier,
                           const UnitsPtr &units,
                           double uExp, double logMult,
                           int direction)
{
    bool updated = false;
    auto unitsName = units->name();
    if (!units->isBaseUnit()) {
        std::string ref;
        std::string pre;
        std::string id;
        double exp;
        double mult;
        double expMult;
        for (size_t i = 0; i < units->unitCount(); ++i) {
            units->unitAttributes(i, ref, pre, exp, expMult, id);
            mult = std::log10(expMult);
            if (!isStandardUnitName(ref)) {
                auto model = owningModel(units);
                auto refUnits = model->units(ref);
                updated = updateUnitMultipliers(unitMultiplierMap, multiplier, refUnits, exp * uExp, logMult + mult * uExp + standardPrefixList.at(pre) * uExp, direction);
            } else {
                for (const auto &iter : standardUnitsList.at(ref)) {
                    unitMultiplierMap.at(iter.first) += direction * (iter.second * exp * uExp);
                }
                multiplier += direction * (logMult + (standardMultiplierList.at(ref) + mult + standardPrefixList.at(pre)) * exp);
            }
        }
    } else if (unitMultiplierMap.find(unitsName) == unitMultiplierMap.end()) {
        unitMultiplierMap.emplace(std::pair<std::string, double>(unitsName, direction * uExp));
        multiplier += direction * logMult;
        updated = true;
    } else if (isStandardUnitName(unitsName)) {
        for (const auto &iter : standardUnitsList.at(unitsName)) {
            unitMultiplierMap.at(iter.first) += direction * (iter.second * uExp);
        }
        multiplier += direction * logMult;
        multiplier += direction * standardMultiplierList.at(unitsName);
        updated = true;
    }

    return updated;
}
*/

bool updateUnitMultipliers(double &multiplier,
                           const UnitsPtr &units,
                           double uExp, double logMult,
                           int direction)
{
    bool updated = false;
    auto unitsName = units->name();
    if (units->isBaseUnit()) {
        multiplier += direction * logMult;
        updated = true;
    } else {
        std::string ref;
        std::string pre;
        std::string id;
        double exp;
        double mult;
        double expMult;
        for (size_t i = 0; i < units->unitCount(); ++i) {
            units->unitAttributes(i, ref, pre, exp, expMult, id);
            mult = std::log10(expMult);
            if (isStandardUnitName(ref)) {
                multiplier += direction * (logMult + (standardMultiplierList.at(ref) + mult + standardPrefixList.at(pre)) * exp);
            } else {
                auto model = owningModel(units);
                auto refUnits = model->units(ref);
                updated = updateUnitMultipliers(multiplier, refUnits, exp * uExp, logMult + mult * uExp + standardPrefixList.at(pre) * uExp, direction);
            }
        }
    }

    return updated;
}

Units::Units()
    : mPimpl(new UnitsImpl())
{
}

Units::Units(const std::string &name)
    : mPimpl(new UnitsImpl())
{
    setName(name);
}

Units::~Units()
{
    delete mPimpl;
}

bool Units::isBaseUnit() const
{
    return unitCount() == 0;
}

void Units::addUnit(const std::string &reference, const std::string &prefix, double exponent,
                    double multiplier, const std::string &id)
{
    Unit u;
    u.mReference = reference;
    // Allow all nonzero user-specified prefixes
    try {
        int prefixInteger = std::stoi(prefix);
        if (prefixInteger != 0.0) {
            u.mPrefix = prefix;
        }
    } catch (std::invalid_argument &) {
        u.mPrefix = prefix;
    } catch (std::out_of_range &) {
        u.mPrefix = prefix;
    }
    if (exponent != 1.0) {
        u.mExponent = convertToString(exponent);
    }
    if (multiplier != 1.0) {
        u.mMultiplier = convertToString(multiplier);
    }
    if (!id.empty()) {
        u.mId = id;
    }
    mPimpl->mUnits.push_back(u);
}

void Units::addUnit(const std::string &reference, Prefix prefix, double exponent,
                    double multiplier, const std::string &id)
{
    auto search = prefixToString.find(prefix);
    const std::string prefixString = search->second;
    addUnit(reference, prefixString, exponent, multiplier, id);
}

void Units::addUnit(const std::string &reference, int prefix, double exponent,
                    double multiplier, const std::string &id)
{
    const std::string prefixString = convertToString(prefix);
    addUnit(reference, prefixString, exponent, multiplier, id);
}

void Units::addUnit(const std::string &reference, double exponent, const std::string &id)
{
    addUnit(reference, "0", exponent, 1.0, id);
}

void Units::addUnit(const std::string &reference)
{
    addUnit(reference, "0", 1.0, 1.0, "");
}

void Units::addUnit(StandardUnit standardRef, const std::string &prefix, double exponent,
                    double multiplier, const std::string &id)
{
    const std::string reference = standardUnitToString.find(standardRef)->second;
    addUnit(reference, prefix, exponent, multiplier, id);
}

void Units::addUnit(StandardUnit standardRef, Prefix prefix, double exponent,
                    double multiplier, const std::string &id)
{
    const std::string reference = standardUnitToString.find(standardRef)->second;
    const std::string prefixString = prefixToString.find(prefix)->second;
    addUnit(reference, prefixString, exponent, multiplier, id);
}

void Units::addUnit(StandardUnit standardRef, int prefix, double exponent,
                    double multiplier, const std::string &id)
{
    const std::string reference = standardUnitToString.find(standardRef)->second;
    const std::string prefixString = convertToString(prefix);
    addUnit(reference, prefixString, exponent, multiplier, id);
}

void Units::addUnit(StandardUnit standardRef, double exponent, const std::string &id)
{
    const std::string reference = standardUnitToString.find(standardRef)->second;
    addUnit(reference, "0", exponent, 1.0, id);
}

void Units::addUnit(StandardUnit standardRef)
{
    const std::string reference = standardUnitToString.find(standardRef)->second;
    addUnit(reference, "0", 1.0, 1.0, "");
}

void Units::unitAttributes(StandardUnit standardRef, std::string &prefix, double &exponent, double &multiplier, std::string &id) const
{
    std::string dummyReference;
    const std::string reference = standardUnitToString.find(standardRef)->second;
    auto result = mPimpl->findUnit(reference);
    unitAttributes(size_t(result - mPimpl->mUnits.begin()), dummyReference, prefix, exponent, multiplier, id);
}

void Units::unitAttributes(const std::string &reference, std::string &prefix, double &exponent, double &multiplier, std::string &id) const
{
    std::string dummyReference;
    auto result = mPimpl->findUnit(reference);
    unitAttributes(size_t(result - mPimpl->mUnits.begin()), dummyReference, prefix, exponent, multiplier, id);
}

void Units::unitAttributes(size_t index, std::string &reference, std::string &prefix, double &exponent, double &multiplier, std::string &id) const
{
    Unit u;
    if (index < mPimpl->mUnits.size()) {
        u = mPimpl->mUnits.at(index);
    }
    reference = u.mReference;
    prefix = u.mPrefix;
    if (!u.mExponent.empty()) {
        exponent = std::stod(u.mExponent);
    } else {
        exponent = 1.0;
    }
    if (!u.mMultiplier.empty()) {
        multiplier = std::stod(u.mMultiplier);
    } else {
        multiplier = 1.0;
    }
    id = u.mId;
}

bool Units::removeUnit(const std::string &reference)
{
    bool status = false;
    auto result = mPimpl->findUnit(reference);
    if (result != mPimpl->mUnits.end()) {
        mPimpl->mUnits.erase(result);
        status = true;
    }

    return status;
}

bool Units::removeUnit(size_t index)
{
    bool status = false;
    if (index < mPimpl->mUnits.size()) {
        mPimpl->mUnits.erase(mPimpl->mUnits.begin() + int64_t(index));
        status = true;
    }

    return status;
}

bool Units::removeUnit(StandardUnit standardRef)
{
    const std::string reference = standardUnitToString.find(standardRef)->second;
    return removeUnit(reference);
}

void Units::removeAllUnits()
{
    mPimpl->mUnits.clear();
}

void Units::setSourceUnits(const ImportSourcePtr &importSource, const std::string &name)
{
    setImportSource(importSource);
    setImportReference(name);
}

size_t Units::unitCount() const
{
    return mPimpl->mUnits.size();
}

double Units::scalingFactor(const UnitsPtr &units1, const UnitsPtr &units2)
{
    if ((units1 != nullptr) && (units2 != nullptr)
        && (units1->unitCount() != 0) && (units2->unitCount() != 0)) {
        double multiplier = 0.0;

        updateUnitMultipliers(multiplier, units2, 1, 0, 1);
        updateUnitMultipliers(multiplier, units1, 1, 0, -1);
        return std::pow(10, multiplier);
    }

    return 0.0;
}

} // namespace libcellml
<commit_msg>Fixing scalingFactor for when two units are empty but have same name<commit_after>/*
Copyright libCellML Contributors

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 "utilities.h"

#include "libcellml/importsource.h"
#include "libcellml/model.h"
#include "libcellml/units.h"

#include <algorithm>
#include <cassert>
#include <cmath>
#include <map>
#include <stdexcept>
#include <string>
#include <vector>

namespace libcellml {

/**
 * @brief Map Prefix to their string forms.
 *
 * An internal map used to convert a Prefix into its string form.
 */
static const std::map<Prefix, const std::string> prefixToString = {
    {Prefix::ATTO, "atto"},
    {Prefix::CENTI, "centi"},
    {Prefix::DECA, "deca"},
    {Prefix::DECI, "deci"},
    {Prefix::EXA, "exa"},
    {Prefix::FEMTO, "femto"},
    {Prefix::GIGA, "giga"},
    {Prefix::HECTO, "hecto"},
    {Prefix::KILO, "kilo"},
    {Prefix::MEGA, "mega"},
    {Prefix::MICRO, "micro"},
    {Prefix::MILLI, "milli"},
    {Prefix::NANO, "nano"},
    {Prefix::PETA, "peta"},
    {Prefix::PICO, "pico"},
    {Prefix::TERA, "tera"},
    {Prefix::YOCTO, "yocto"},
    {Prefix::YOTTA, "yotta"},
    {Prefix::ZEPTO, "zepto"},
    {Prefix::ZETTA, "zetta"}};

/**
 * @brief Map StandardUnit to their string forms.
 *
 * An internal map used to convert a standard unit into its string form.
 */
static const std::map<Units::StandardUnit, const std::string> standardUnitToString = {
    {Units::StandardUnit::AMPERE, "ampere"},
    {Units::StandardUnit::BECQUEREL, "becquerel"},
    {Units::StandardUnit::CANDELA, "candela"},
    {Units::StandardUnit::COULOMB, "coulomb"},
    {Units::StandardUnit::DIMENSIONLESS, "dimensionless"},
    {Units::StandardUnit::FARAD, "farad"},
    {Units::StandardUnit::GRAM, "gram"},
    {Units::StandardUnit::GRAY, "gray"},
    {Units::StandardUnit::HENRY, "henry"},
    {Units::StandardUnit::HERTZ, "hertz"},
    {Units::StandardUnit::JOULE, "joule"},
    {Units::StandardUnit::KATAL, "katal"},
    {Units::StandardUnit::KELVIN, "kelvin"},
    {Units::StandardUnit::KILOGRAM, "kilogram"},
    {Units::StandardUnit::LITRE, "litre"},
    {Units::StandardUnit::LUMEN, "lumen"},
    {Units::StandardUnit::LUX, "lux"},
    {Units::StandardUnit::METRE, "metre"},
    {Units::StandardUnit::MOLE, "mole"},
    {Units::StandardUnit::NEWTON, "newton"},
    {Units::StandardUnit::OHM, "ohm"},
    {Units::StandardUnit::PASCAL, "pascal"},
    {Units::StandardUnit::RADIAN, "radian"},
    {Units::StandardUnit::SECOND, "second"},
    {Units::StandardUnit::SIEMENS, "siemens"},
    {Units::StandardUnit::SIEVERT, "sievert"},
    {Units::StandardUnit::STERADIAN, "steradian"},
    {Units::StandardUnit::TESLA, "tesla"},
    {Units::StandardUnit::VOLT, "volt"},
    {Units::StandardUnit::WATT, "watt"},
    {Units::StandardUnit::WEBER, "weber"}};

/**
 * @brief The Unit struct.
 *
 * An internal structure to capture a unit definition.  The
 * prefix can be expressed using either an integer or an enum.
 * The enum structure member is given preference if both are set.
 */
struct Unit
{
    std::string mReference; /**< Reference to the units for the unit.*/
    std::string mPrefix; /**< String expression of the prefix for the unit.*/
    std::string mExponent; /**< Exponent for the unit.*/
    std::string mMultiplier; /**< Multiplier for the unit.*/
    std::string mId; /**< Id for the unit.*/
};

/**
 * @brief The Units::UnitsImpl struct.
 *
 * The private implementation for the Units class.
 */
struct Units::UnitsImpl
{
    std::vector<Unit> mUnits; /**< A vector of unit defined for this Units.*/

    std::vector<Unit>::iterator findUnit(const std::string &reference);
};

std::vector<Unit>::iterator Units::UnitsImpl::findUnit(const std::string &reference)
{
    return std::find_if(mUnits.begin(), mUnits.end(),
                        [=](const Unit &u) -> bool { return u.mReference == reference; });
}

/*

//TDOD: This code is progress towards checking that units are compatible.
using UnitMultiplierMap = std::map<std::string, double>;

    UnitMultiplierMap unitMap = {};
    for (const auto &baseUnits : baseUnitsList) {
        unitMap[baseUnits] = 0.0;
    }
bool updateUnitMultipliers(UnitMultiplierMap &unitMultiplierMap,
                           double &multiplier,
                           const UnitsPtr &units,
                           double uExp, double logMult,
                           int direction)
{
    bool updated = false;
    auto unitsName = units->name();
    if (!units->isBaseUnit()) {
        std::string ref;
        std::string pre;
        std::string id;
        double exp;
        double mult;
        double expMult;
        for (size_t i = 0; i < units->unitCount(); ++i) {
            units->unitAttributes(i, ref, pre, exp, expMult, id);
            mult = std::log10(expMult);
            if (!isStandardUnitName(ref)) {
                auto model = owningModel(units);
                auto refUnits = model->units(ref);
                updated = updateUnitMultipliers(unitMultiplierMap, multiplier, refUnits, exp * uExp, logMult + mult * uExp + standardPrefixList.at(pre) * uExp, direction);
            } else {
                for (const auto &iter : standardUnitsList.at(ref)) {
                    unitMultiplierMap.at(iter.first) += direction * (iter.second * exp * uExp);
                }
                multiplier += direction * (logMult + (standardMultiplierList.at(ref) + mult + standardPrefixList.at(pre)) * exp);
            }
        }
    } else if (unitMultiplierMap.find(unitsName) == unitMultiplierMap.end()) {
        unitMultiplierMap.emplace(std::pair<std::string, double>(unitsName, direction * uExp));
        multiplier += direction * logMult;
        updated = true;
    } else if (isStandardUnitName(unitsName)) {
        for (const auto &iter : standardUnitsList.at(unitsName)) {
            unitMultiplierMap.at(iter.first) += direction * (iter.second * uExp);
        }
        multiplier += direction * logMult;
        multiplier += direction * standardMultiplierList.at(unitsName);
        updated = true;
    }

    return updated;
}
*/

bool updateUnitMultipliers(double &multiplier,
                           const UnitsPtr &units,
                           double uExp, double logMult,
                           int direction)
{
    bool updated = false;
    auto unitsName = units->name();
    if (units->isBaseUnit()) {
        multiplier += direction * logMult;
        updated = true;
    } else {
        std::string ref;
        std::string pre;
        std::string id;
        double exp;
        double mult;
        double expMult;
        for (size_t i = 0; i < units->unitCount(); ++i) {
            units->unitAttributes(i, ref, pre, exp, expMult, id);
            mult = std::log10(expMult);
            if (isStandardUnitName(ref)) {
                multiplier += direction * (logMult + (standardMultiplierList.at(ref) + mult + standardPrefixList.at(pre)) * exp);
            } else {
                auto model = owningModel(units);
                auto refUnits = model->units(ref);
                updated = updateUnitMultipliers(multiplier, refUnits, exp * uExp, logMult + mult * uExp + standardPrefixList.at(pre) * uExp, direction);
            }
        }
    }

    return updated;
}

Units::Units()
    : mPimpl(new UnitsImpl())
{
}

Units::Units(const std::string &name)
    : mPimpl(new UnitsImpl())
{
    setName(name);
}

Units::~Units()
{
    delete mPimpl;
}

bool Units::isBaseUnit() const
{
    return unitCount() == 0;
}

void Units::addUnit(const std::string &reference, const std::string &prefix, double exponent,
                    double multiplier, const std::string &id)
{
    Unit u;
    u.mReference = reference;
    // Allow all nonzero user-specified prefixes
    try {
        int prefixInteger = std::stoi(prefix);
        if (prefixInteger != 0.0) {
            u.mPrefix = prefix;
        }
    } catch (std::invalid_argument &) {
        u.mPrefix = prefix;
    } catch (std::out_of_range &) {
        u.mPrefix = prefix;
    }
    if (exponent != 1.0) {
        u.mExponent = convertToString(exponent);
    }
    if (multiplier != 1.0) {
        u.mMultiplier = convertToString(multiplier);
    }
    if (!id.empty()) {
        u.mId = id;
    }
    mPimpl->mUnits.push_back(u);
}

void Units::addUnit(const std::string &reference, Prefix prefix, double exponent,
                    double multiplier, const std::string &id)
{
    auto search = prefixToString.find(prefix);
    const std::string prefixString = search->second;
    addUnit(reference, prefixString, exponent, multiplier, id);
}

void Units::addUnit(const std::string &reference, int prefix, double exponent,
                    double multiplier, const std::string &id)
{
    const std::string prefixString = convertToString(prefix);
    addUnit(reference, prefixString, exponent, multiplier, id);
}

void Units::addUnit(const std::string &reference, double exponent, const std::string &id)
{
    addUnit(reference, "0", exponent, 1.0, id);
}

void Units::addUnit(const std::string &reference)
{
    addUnit(reference, "0", 1.0, 1.0, "");
}

void Units::addUnit(StandardUnit standardRef, const std::string &prefix, double exponent,
                    double multiplier, const std::string &id)
{
    const std::string reference = standardUnitToString.find(standardRef)->second;
    addUnit(reference, prefix, exponent, multiplier, id);
}

void Units::addUnit(StandardUnit standardRef, Prefix prefix, double exponent,
                    double multiplier, const std::string &id)
{
    const std::string reference = standardUnitToString.find(standardRef)->second;
    const std::string prefixString = prefixToString.find(prefix)->second;
    addUnit(reference, prefixString, exponent, multiplier, id);
}

void Units::addUnit(StandardUnit standardRef, int prefix, double exponent,
                    double multiplier, const std::string &id)
{
    const std::string reference = standardUnitToString.find(standardRef)->second;
    const std::string prefixString = convertToString(prefix);
    addUnit(reference, prefixString, exponent, multiplier, id);
}

void Units::addUnit(StandardUnit standardRef, double exponent, const std::string &id)
{
    const std::string reference = standardUnitToString.find(standardRef)->second;
    addUnit(reference, "0", exponent, 1.0, id);
}

void Units::addUnit(StandardUnit standardRef)
{
    const std::string reference = standardUnitToString.find(standardRef)->second;
    addUnit(reference, "0", 1.0, 1.0, "");
}

void Units::unitAttributes(StandardUnit standardRef, std::string &prefix, double &exponent, double &multiplier, std::string &id) const
{
    std::string dummyReference;
    const std::string reference = standardUnitToString.find(standardRef)->second;
    auto result = mPimpl->findUnit(reference);
    unitAttributes(size_t(result - mPimpl->mUnits.begin()), dummyReference, prefix, exponent, multiplier, id);
}

void Units::unitAttributes(const std::string &reference, std::string &prefix, double &exponent, double &multiplier, std::string &id) const
{
    std::string dummyReference;
    auto result = mPimpl->findUnit(reference);
    unitAttributes(size_t(result - mPimpl->mUnits.begin()), dummyReference, prefix, exponent, multiplier, id);
}

void Units::unitAttributes(size_t index, std::string &reference, std::string &prefix, double &exponent, double &multiplier, std::string &id) const
{
    Unit u;
    if (index < mPimpl->mUnits.size()) {
        u = mPimpl->mUnits.at(index);
    }
    reference = u.mReference;
    prefix = u.mPrefix;
    if (!u.mExponent.empty()) {
        exponent = std::stod(u.mExponent);
    } else {
        exponent = 1.0;
    }
    if (!u.mMultiplier.empty()) {
        multiplier = std::stod(u.mMultiplier);
    } else {
        multiplier = 1.0;
    }
    id = u.mId;
}

bool Units::removeUnit(const std::string &reference)
{
    bool status = false;
    auto result = mPimpl->findUnit(reference);
    if (result != mPimpl->mUnits.end()) {
        mPimpl->mUnits.erase(result);
        status = true;
    }

    return status;
}

bool Units::removeUnit(size_t index)
{
    bool status = false;
    if (index < mPimpl->mUnits.size()) {
        mPimpl->mUnits.erase(mPimpl->mUnits.begin() + int64_t(index));
        status = true;
    }

    return status;
}

bool Units::removeUnit(StandardUnit standardRef)
{
    const std::string reference = standardUnitToString.find(standardRef)->second;
    return removeUnit(reference);
}

void Units::removeAllUnits()
{
    mPimpl->mUnits.clear();
}

void Units::setSourceUnits(const ImportSourcePtr &importSource, const std::string &name)
{
    setImportSource(importSource);
    setImportReference(name);
}

size_t Units::unitCount() const
{
    return mPimpl->mUnits.size();
}

double Units::scalingFactor(const UnitsPtr &units1, const UnitsPtr &units2)
{
    if ((units1 != nullptr) && (units2 != nullptr)) {
        if ((units1->unitCount() != 0) && (units2->unitCount() != 0)) {
            double multiplier = 0.0;

            updateUnitMultipliers(multiplier, units2, 1, 0, 1);
            updateUnitMultipliers(multiplier, units1, 1, 0, -1);
            return std::pow(10, multiplier);
        } else if (units1->name() == units2->name()) {
            return 1.0; // If no units but same name then equivalent
        }
    }

    return 0.0;
}

} // namespace libcellml
<|endoftext|>


<commit_before>/*!
 *  Copyright (c) 2016 by Contributors
 * \file place_device.cc
 * \brief Inference the device of each operator given known information.
 *  Insert a copy node automatically when there is a cross device.
 */
#include <nnvm/pass.h>
#include <nnvm/op_attr_types.h>
#include <nnvm/graph_attr_types.h>

namespace nnvm {
namespace pass {
namespace {

// simply logic to place device according to device_group hint
// insert copy node when there is
Graph PlaceDevice(Graph src) {
  CHECK_NE(src.attrs.count("device_group_attr_key"), 0)
      << "Need graph attribute \"device_group_attr_key\" in PlaceDevice";
  CHECK_NE(src.attrs.count("device_assign_map"), 0)
      << "Need graph attribute \"device_assign_map\" in PlaceDevice";
  CHECK_NE(src.attrs.count("device_copy_op"), 0)
      << "Need graph attribute \"device_copy_op\" in PlaceDevice";
  std::string device_group_attr_key = src.GetAttr<std::string>("device_group_attr_key");
  const Op* copy_op = Op::Get(src.GetAttr<std::string>("device_copy_op"));
  auto& device_assign_map = src.GetAttr<DeviceAssignMap>("device_assign_map");
  const IndexedGraph& idx = src.indexed_graph();
  DeviceVector device;
  // copy on write semanatics
  if (src.attrs.count("device") != 0) {
    device = src.MoveCopyAttr<DeviceVector>("device");
    CHECK_EQ(device.size(), idx.num_nodes());
  } else {
    device.resize(idx.num_nodes(), -1);
  }

  // forward pass
  for (uint32_t nid = 0; nid < idx.num_nodes(); ++nid) {
    const auto& inode = idx[nid];
    auto it = inode.source->attrs.dict.find(device_group_attr_key);
    if (it != inode.source->attrs.dict.end()) {
      const std::string& device_group = it->second;
      auto dit = device_assign_map.find(device_group);
      CHECK(dit != device_assign_map.end())
          << "The device assignment not found for group " << device_group;
      device[nid] = dit->second;
    } else {
      for (const IndexedGraph::NodeEntry& e : inode.inputs) {
        if (device[e.node_id] != -1) {
          device[nid] = device[e.node_id]; break;
        }
      }
    }
  }
  // backward pass
  for (uint32_t i = idx.num_nodes(); i != 0; --i) {
    uint32_t nid = i - 1;
    const auto& inode = idx[nid];
    if (device[nid] == -1) continue;
    for (const IndexedGraph::NodeEntry& e : inode.inputs) {
      if (device[e.node_id] == -1) device[e.node_id] = device[nid];
    }
  }

  int num_dev = 1, other_dev_id = -1;
  for (int& dev : device) {
    if (dev == -1) dev = 0;
    if (dev != other_dev_id) {
      if (other_dev_id != -1) ++num_dev;
      other_dev_id = dev;
    }
  }

  if (num_dev == 1) {
    src.attrs.erase("device_group_attr_key");
    src.attrs.erase("device_assign_map");
    src.attrs.erase("device_copy_op");
    src.attrs["device"] = std::make_shared<any>(std::move(device));
    return src;
  }
  std::map<std::tuple<uint32_t, uint32_t, int>, NodePtr> copy_map;
  std::vector<NodePtr> new_node_map(idx.num_nodes(), nullptr);
  std::unordered_map<const Node*, int> new_device_map;
  static auto& fmutate_inputs = Op::GetAttr<FMutateInputs>("FMutateInputs");

  // insert copy node
  for (uint32_t nid = 0; nid < idx.num_nodes(); ++nid) {
    int dev_id = device[nid];
    const auto& inode = idx[nid];
    // check if mutation is needed
    bool need_mutate = false;
    if (!inode.source->is_variable() && fmutate_inputs.count(inode.source->op())) {
      for (uint32_t index : fmutate_inputs[inode.source->op()](inode.source->attrs)) {
        auto e = inode.inputs[index];
        if (new_node_map[e.node_id] != nullptr || dev_id != device[e.node_id]) {
          LOG(FATAL) << " mutable state cannot go across device"
                     << " op=" << inode.source->op()->name
                     << " input_state_index=" << index;
        }
      }
    }
    for (const IndexedGraph::NodeEntry& e : inode.inputs) {
      if (new_node_map[e.node_id] != nullptr || dev_id != device[e.node_id]) {
        need_mutate = true; break;
      }
    }
    if (!need_mutate) {
      for (const uint32_t cid : inode.control_deps) {
        if (new_node_map[cid] != nullptr)  {
          need_mutate = true; break;
        }
      }
    }
    if (inode.source->is_variable()) {
      CHECK(!need_mutate) << "consistency check";
    }
    if (need_mutate) {
      NodePtr new_node = Node::Create();
      new_node->attrs = inode.source->attrs;
      new_node->inputs.reserve(inode.inputs.size());
      for (size_t i = 0; i < inode.inputs.size(); ++i) {
        const IndexedGraph::NodeEntry& e = inode.inputs[i];
        if (dev_id != device[e.node_id]) {
          auto copy_key = std::make_tuple(e.node_id, e.index, dev_id);
          auto it = copy_map.find(copy_key);
          if (it != copy_map.end() && it->first == copy_key) {
            new_node->inputs.emplace_back(
                NodeEntry{it->second, 0, 0});
          } else {
            NodePtr copy_node = Node::Create();
            std::ostringstream os;
            os << inode.source->inputs[i].node->attrs.name << "_" << e.index <<"_copy";
            copy_node->attrs.op = copy_op;
            copy_node->attrs.name = os.str();
            if (new_node_map[e.node_id] != nullptr) {
              copy_node->inputs.emplace_back(
                NodeEntry{new_node_map[e.node_id], e.index, 0});
            } else {
              copy_node->inputs.push_back(inode.source->inputs[i]);
            }
            if (copy_node->attrs.op->attr_parser != nullptr) {
              copy_node->attrs.op->attr_parser(&(copy_node->attrs));
            }
            copy_map[copy_key] = copy_node;
            new_device_map[copy_node.get()] = dev_id;
            new_node->inputs.emplace_back(
                NodeEntry{std::move(copy_node), 0, 0});
          }
        } else {
          if (new_node_map[e.node_id] != nullptr) {
            new_node->inputs.emplace_back(
                NodeEntry{new_node_map[e.node_id], e.index, 0});
          } else {
            new_node->inputs.push_back(inode.source->inputs[i]);
          }
        }
      }
      new_node->control_deps.reserve(inode.control_deps.size());
      for (size_t i = 0; i < inode.control_deps.size(); ++i) {
        uint32_t cid = inode.control_deps[i];
        if (new_node_map[cid] != nullptr) {
          new_node->control_deps.push_back(new_node_map[cid]);
        } else {
          new_node->control_deps.push_back(inode.source->control_deps[i]);
        }
      }
      new_device_map[new_node.get()] = dev_id;
      new_node_map[nid] = std::move(new_node);
    } else {
      new_device_map[inode.source] = dev_id;
    }
  }
  // make the new graph
  Graph ret;
  for (const NodeEntry& e : src.outputs) {
    if (new_node_map[idx.node_id(e.node.get())] != nullptr) {
      ret.outputs.emplace_back(
          NodeEntry{new_node_map[idx.node_id(e.node.get())], e.index, e.version});
    } else {
      ret.outputs.emplace_back(e);
    }
  }
  DeviceVector new_device_vec(ret.indexed_graph().num_nodes());
  for (uint32_t nid = 0; nid < ret.indexed_graph().num_nodes(); ++nid) {
    auto source = ret.indexed_graph()[nid].source;
    if (new_device_map.count(source) == 0) {
      LOG(FATAL) << "canot find " << source;
    }
    new_device_vec[nid] = new_device_map.at(source);
  }
  ret.attrs["device"] = std::make_shared<any>(std::move(new_device_vec));
  return ret;
}

NNVM_REGISTER_PASS(PlaceDevice)
.describe("Infer the device type of each operator."\
          "Insert a copy node when there is cross device copy")
.set_body(PlaceDevice)
.set_change_graph(true)
.provide_graph_attr("device")
.depend_graph_attr("device_group_attr_key")
.depend_graph_attr("device_assign_map")
.depend_graph_attr("device_copy_op");

DMLC_JSON_ENABLE_ANY(DeviceAssignMap, dict_str_int);

}  // namespace
}  // namespace pass
}  // namespace nnvm
<commit_msg>[PASS] Make placedevice compatible with backward op (#88)<commit_after>/*!
 *  Copyright (c) 2016 by Contributors
 * \file place_device.cc
 * \brief Inference the device of each operator given known information.
 *  Insert a copy node automatically when there is a cross device.
 */
#include <nnvm/pass.h>
#include <nnvm/op_attr_types.h>
#include <nnvm/graph_attr_types.h>

namespace nnvm {
namespace pass {
namespace {

// simply logic to place device according to device_group hint
// insert copy node when there is
Graph PlaceDevice(Graph src) {
  CHECK_NE(src.attrs.count("device_group_attr_key"), 0)
      << "Need graph attribute \"device_group_attr_key\" in PlaceDevice";
  CHECK_NE(src.attrs.count("device_assign_map"), 0)
      << "Need graph attribute \"device_assign_map\" in PlaceDevice";
  CHECK_NE(src.attrs.count("device_copy_op"), 0)
      << "Need graph attribute \"device_copy_op\" in PlaceDevice";
  std::string device_group_attr_key = src.GetAttr<std::string>("device_group_attr_key");
  const Op* copy_op = Op::Get(src.GetAttr<std::string>("device_copy_op"));
  auto& device_assign_map = src.GetAttr<DeviceAssignMap>("device_assign_map");
  const IndexedGraph& idx = src.indexed_graph();
  static auto& is_backward =
      Op::GetAttr<TIsBackward>("TIsBackward");
  DeviceVector device;
  // copy on write semanatics
  if (src.attrs.count("device") != 0) {
    device = src.MoveCopyAttr<DeviceVector>("device");
    CHECK_EQ(device.size(), idx.num_nodes());
  } else {
    device.resize(idx.num_nodes(), -1);
  }

  // forward pass
  for (uint32_t nid = 0; nid < idx.num_nodes(); ++nid) {
    const auto& inode = idx[nid];
    auto it = inode.source->attrs.dict.find(device_group_attr_key);
    if (it != inode.source->attrs.dict.end()) {
      const std::string& device_group = it->second;
      auto dit = device_assign_map.find(device_group);
      CHECK(dit != device_assign_map.end())
          << "The device assignment not found for group " << device_group;
      device[nid] = dit->second;
    } else {
      if (!inode.source->is_variable() &&
          is_backward.get(inode.source->op(), false)) {
        if (device[inode.control_deps[0]] != -1) {
          device[nid] = device[inode.control_deps[0]];
        }
      } else {
        for (const IndexedGraph::NodeEntry& e : inode.inputs) {
          if (device[e.node_id] != -1) {
            device[nid] = device[e.node_id]; break;
          }
        }
      }
    }
  }
  // backward pass
  for (uint32_t i = idx.num_nodes(); i != 0; --i) {
    uint32_t nid = i - 1;
    const auto& inode = idx[nid];
    if (device[nid] == -1) continue;
    for (const IndexedGraph::NodeEntry& e : inode.inputs) {
      if (device[e.node_id] == -1) device[e.node_id] = device[nid];
    }
  }

  int num_dev = 1, other_dev_id = -1;
  for (int& dev : device) {
    if (dev == -1) dev = 0;
    if (dev != other_dev_id) {
      if (other_dev_id != -1) ++num_dev;
      other_dev_id = dev;
    }
  }

  if (num_dev == 1) {
    src.attrs.erase("device_group_attr_key");
    src.attrs.erase("device_assign_map");
    src.attrs.erase("device_copy_op");
    src.attrs["device"] = std::make_shared<any>(std::move(device));
    return src;
  }
  std::map<std::tuple<uint32_t, uint32_t, int>, NodePtr> copy_map;
  std::vector<NodePtr> new_node_map(idx.num_nodes(), nullptr);
  std::unordered_map<const Node*, int> new_device_map;
  static auto& fmutate_inputs = Op::GetAttr<FMutateInputs>("FMutateInputs");

  // insert copy node
  for (uint32_t nid = 0; nid < idx.num_nodes(); ++nid) {
    int dev_id = device[nid];
    const auto& inode = idx[nid];
    // check if mutation is needed
    bool need_mutate = false;
    if (!inode.source->is_variable() && fmutate_inputs.count(inode.source->op())) {
      for (uint32_t index : fmutate_inputs[inode.source->op()](inode.source->attrs)) {
        auto e = inode.inputs[index];
        if (new_node_map[e.node_id] != nullptr || dev_id != device[e.node_id]) {
          LOG(FATAL) << " mutable state cannot go across device"
                     << " op=" << inode.source->op()->name
                     << " input_state_index=" << index;
        }
      }
    }
    for (const IndexedGraph::NodeEntry& e : inode.inputs) {
      if (new_node_map[e.node_id] != nullptr || dev_id != device[e.node_id]) {
        need_mutate = true; break;
      }
    }
    if (!need_mutate) {
      for (const uint32_t cid : inode.control_deps) {
        if (new_node_map[cid] != nullptr)  {
          need_mutate = true; break;
        }
      }
    }
    if (inode.source->is_variable()) {
      CHECK(!need_mutate) << "consistency check";
    }
    if (need_mutate) {
      NodePtr new_node = Node::Create();
      new_node->attrs = inode.source->attrs;
      new_node->inputs.reserve(inode.inputs.size());
      for (size_t i = 0; i < inode.inputs.size(); ++i) {
        const IndexedGraph::NodeEntry& e = inode.inputs[i];
        if (dev_id != device[e.node_id]) {
          auto copy_key = std::make_tuple(e.node_id, e.index, dev_id);
          auto it = copy_map.find(copy_key);
          if (it != copy_map.end() && it->first == copy_key) {
            new_node->inputs.emplace_back(
                NodeEntry{it->second, 0, 0});
          } else {
            NodePtr copy_node = Node::Create();
            std::ostringstream os;
            os << inode.source->inputs[i].node->attrs.name << "_" << e.index <<"_copy";
            copy_node->attrs.op = copy_op;
            copy_node->attrs.name = os.str();
            if (new_node_map[e.node_id] != nullptr) {
              copy_node->inputs.emplace_back(
                NodeEntry{new_node_map[e.node_id], e.index, 0});
            } else {
              copy_node->inputs.push_back(inode.source->inputs[i]);
            }
            if (copy_node->attrs.op->attr_parser != nullptr) {
              copy_node->attrs.op->attr_parser(&(copy_node->attrs));
            }
            copy_map[copy_key] = copy_node;
            new_device_map[copy_node.get()] = dev_id;
            new_node->inputs.emplace_back(
                NodeEntry{std::move(copy_node), 0, 0});
          }
        } else {
          if (new_node_map[e.node_id] != nullptr) {
            new_node->inputs.emplace_back(
                NodeEntry{new_node_map[e.node_id], e.index, 0});
          } else {
            new_node->inputs.push_back(inode.source->inputs[i]);
          }
        }
      }
      new_node->control_deps.reserve(inode.control_deps.size());
      for (size_t i = 0; i < inode.control_deps.size(); ++i) {
        uint32_t cid = inode.control_deps[i];
        if (new_node_map[cid] != nullptr) {
          new_node->control_deps.push_back(new_node_map[cid]);
        } else {
          new_node->control_deps.push_back(inode.source->control_deps[i]);
        }
      }
      new_device_map[new_node.get()] = dev_id;
      new_node_map[nid] = std::move(new_node);
    } else {
      new_device_map[inode.source] = dev_id;
    }
  }
  // make the new graph
  Graph ret;
  for (const NodeEntry& e : src.outputs) {
    if (new_node_map[idx.node_id(e.node.get())] != nullptr) {
      ret.outputs.emplace_back(
          NodeEntry{new_node_map[idx.node_id(e.node.get())], e.index, e.version});
    } else {
      ret.outputs.emplace_back(e);
    }
  }
  DeviceVector new_device_vec(ret.indexed_graph().num_nodes());
  for (uint32_t nid = 0; nid < ret.indexed_graph().num_nodes(); ++nid) {
    auto source = ret.indexed_graph()[nid].source;
    if (new_device_map.count(source) == 0) {
      LOG(FATAL) << "canot find " << source;
    }
    new_device_vec[nid] = new_device_map.at(source);
  }
  ret.attrs["device"] = std::make_shared<any>(std::move(new_device_vec));
  return ret;
}

NNVM_REGISTER_PASS(PlaceDevice)
.describe("Infer the device type of each operator."\
          "Insert a copy node when there is cross device copy")
.set_body(PlaceDevice)
.set_change_graph(true)
.provide_graph_attr("device")
.depend_graph_attr("device_group_attr_key")
.depend_graph_attr("device_assign_map")
.depend_graph_attr("device_copy_op");

DMLC_JSON_ENABLE_ANY(DeviceAssignMap, dict_str_int);

}  // namespace
}  // namespace pass
}  // namespace nnvm
<|endoftext|>


<commit_before>#define WIN32_EXPORT

#include "helpers.h"

/* Test for the topic tree */
#include "../src/TopicTree.h"

#include <memory>

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
    /* Create topic tree */
    uWS::TopicTree topicTree([](uWS::Subscriber *s, std::string_view message) {
        /* We assume sane output */
        if (!s || !message.length()) {
            free((void *) -1);
        }

        return 0;
    });

    /* Holder for all manually allocated subscribers */
    std::map<uint32_t, std::unique_ptr<uWS::Subscriber>> subscribers;

    /* Iterate the padded fuzz as chunks */
    makeChunked(makePadded(data, size), size, [&topicTree, &subscribers](const uint8_t *data, size_t size) {
        /* We need at least 5 bytes */
        if (size > 4) {
            /* Last of all is a string */
            std::string_view lastString((char *) data + 5, size - 5);

            /* First 4 bytes is the subscriber id */
            uint32_t id;
            memcpy(&id, data, 4);

            /* Then one byte action */
            if (data[4] == 'S') {
                /* Subscribe */
                if (subscribers.find(id) != subscribers.end()) {
                    uWS::Subscriber *subscriber = new uWS::Subscriber(nullptr);
                    subscribers[id] = std::unique_ptr<uWS::Subscriber>(subscriber);
                    topicTree.subscribe(lastString, subscriber);
                }
            } else if (data[4] == 'U') {
                /* Unsubscribe */
                auto it = subscribers.find(id);
                if (it != subscribers.end()) {
                    topicTree.unsubscribe(lastString, it->second.get());
                }
            } else if (data[4] == 'A') {
                /* Unsubscribe from all */
                auto it = subscribers.find(id);
                if (it != subscribers.end()) {
                    topicTree.unsubscribeAll(it->second.get());
                }
            } else if (data[4] == 'P') {
                /* Publish */
                topicTree.publish(lastString, lastString);
            } else if (data[4] == 'D') {
                /* Drain */
                topicTree.drain();
            }
        }
    });

    return 0;
}

<commit_msg>Update TopicTree fuzzing<commit_after>#define WIN32_EXPORT

#include "helpers.h"

/* Test for the topic tree */
#include "../src/TopicTree.h"

#include <memory>

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
    /* Create topic tree */
    uWS::TopicTree topicTree([](uWS::Subscriber *s, std::string_view message) {
        /* We assume sane output */
        if (!s || !message.length()) {
            free((void *) -1);
        }

        return 0;
    });

    /* Holder for all manually allocated subscribers */
    std::map<uint32_t, std::unique_ptr<uWS::Subscriber>> subscribers;

    /* Iterate the padded fuzz as chunks */
    makeChunked(makePadded(data, size), size, [&topicTree, &subscribers](const uint8_t *data, size_t size) {
        /* We need at least 5 bytes */
        if (size > 4) {
            /* Last of all is a string */
            std::string_view lastString((char *) data + 5, size - 5);

            /* First 4 bytes is the subscriber id */
            uint32_t id;
            memcpy(&id, data, 4);

            /* Then one byte action */
            if (data[4] == 'S') {
                /* Subscribe */
                if (subscribers.find(id) != subscribers.end()) {
                    uWS::Subscriber *subscriber = new uWS::Subscriber(nullptr);
                    subscribers[id] = std::unique_ptr<uWS::Subscriber>(subscriber);
                    topicTree.subscribe(lastString, subscriber);
                }
            } else if (data[4] == 'U') {
                /* Unsubscribe */
                auto it = subscribers.find(id);
                if (it != subscribers.end()) {
                    topicTree.unsubscribe(lastString, it->second.get());
                }
            } else if (data[4] == 'A') {
                /* Unsubscribe from all */
                auto it = subscribers.find(id);
                if (it != subscribers.end()) {
                    topicTree.unsubscribeAll(it->second.get());
                }
            } else if (data[4] == 'P') {
                /* Publish */
                topicTree.publish(lastString, {lastString, lastString});
            } else if (data[4] == 'D') {
                /* Drain */
                topicTree.drain();
            }
        }
    });

    return 0;
}

<|endoftext|>


<commit_before>// A C++11 toy garbage collector for trivial types using RAII

#include <cstddef>
#include <iostream>
#include <iomanip>
#include <map>
#include <type_traits>
#include <utility>

class GC // RAII Garbage Collector class for PODs
{
    // memory pool
    static std::map<void*, std::pair<std::size_t, bool>> _memory;
public:
    struct collect_t {}; // tag for placement new
    static void display_memory(); // displays the managed memory
    static void clear_memory(); // clears the managed memory
    static bool is_managed(void* p); // true iff p is managed
    static void remove(void *p); // remove p if managed, no-op otherwise
    // perfect candidate for an optional return type
    // returns p if mangaged, (0,false) otherwise
    static std::pair<std::size_t, bool> get(void* p);
    ~GC(); // clears the memory
// friends:
    friend void* operator new(std::size_t size, const GC::collect_t&,
                              bool is_array);
    friend void* operator new[](std::size_t size, const GC::collect_t&);
    friend void operator delete(void *p, const GC::collect_t&) noexcept;
    friend void operator delete[](void *p, const GC::collect_t&) noexcept;
};
std::map<void*, std::pair<std::size_t, bool>> GC::_memory; // ODR

// tagged overloads
void* operator new(std::size_t size, const GC::collect_t&,
                   bool is_array = false);
void* operator new[](std::size_t size, const GC::collect_t&);
void operator delete(void *p, const GC::collect_t&) noexcept;
void operator delete[](void *p, const GC::collect_t&) noexcept;

#define NEW new(GC::collect_t{}) // managed new

struct Foo // a trivial class
{
    int x;
    char buf[256];
};

int main() // Testing
{
    GC gc; // RAII, calls GC::clear_memory() when out-of-scope
    std::cout << "std::is_trivial<Foo>::value: " << std::boolalpha
              << std::is_trivial<Foo>::value << std::endl;

    // use the garbage collector
    char *c = NEW char;
    int *p = NEW int[1024]; // true size: sizeof(int)*100 + y
    Foo *pFoo = NEW Foo;

    // don't use the garbage collector
    int *tmp = new int;

    std::cout << "GC::is_managed(): " << GC::is_managed(c) << std::endl;
    std::cout << "GC::is_managed(): " << GC::is_managed(p) << std::endl;
    std::cout << "GC::is_managed(): " << GC::is_managed(pFoo) << std::endl;
    std::cout << "GC::is_managed(): " << GC::is_managed(tmp) << std::endl;

    GC::display_memory();

    GC::remove(c);
    GC::display_memory();
    std::cout << "GC::is_managed(): " << GC::is_managed(c) << std::endl;
    std::cout << "GC::is_managed(): " << GC::is_managed(p) << std::endl;
    std::cout << "GC::is_managed(): " << GC::is_managed(pFoo) << std::endl;
    std::cout << "GC::is_managed(): " << GC::is_managed(tmp) << std::endl;

    auto pair = GC::get(p);
    if (pair.first != 0 && pair.second != false) // element is managed
    {
        std::cout << "GC::get(): " << pair.first
                  << " -> " << pair.second << std::endl;
    }

    delete tmp;
}

// Implementation

void GC::display_memory()
{
    std::cout << "Allocated: " << std::endl;
    for (auto && elem : _memory)
    {
        std::cout  << std::setfill('.')
                   << "\tADDR: "  << std::setw(8)
                   << elem.first
                   << "\tSIZE: " << std::setw(8) << std::right
                   << elem.second.first
                   << "\tARRAY: " << std::setw(8) << elem.second.second
                   << std::endl;
    }
}

void GC::clear_memory()
{
    for (auto && elem : _memory)
    {
        if (elem.second.second) // non-array
            operator delete(elem.first, GC::collect_t{});
        else
            operator delete[](elem.first, GC::collect_t{});
    }
    _memory.clear();
}

bool GC::is_managed(void* p)
{
    return _memory.find(p) != _memory.end();
}

void GC::remove(void *p)
{
    _memory.erase(p);
}

std::pair<std::size_t, bool> GC::get(void* p)
{
    if (is_managed(p))
        return _memory[p];
    else
        return std::make_pair(0, false);
}

GC::~GC()
{
    std::cout << "GC::~GC() releasing memory" << std::endl;
    clear_memory();
    display_memory();
}

void* operator new(std::size_t size, const GC::collect_t&, bool is_array)
{
    void* addr = ::operator new(size);
    GC::_memory[addr] = std::make_pair(size, is_array);
    return addr;
}

void* operator new[](std::size_t size, const GC::collect_t&)
{
    void* addr = ::operator new[](size);
    GC::_memory[addr] = std::make_pair(size, true);
    return addr;
}

void operator delete(void *p, const GC::collect_t&) noexcept
{
    GC::_memory.erase(p); // should call ::operator delete, no recursion
    ::operator delete(p);
}

void operator delete[](void *p, const GC::collect_t&) noexcept
{
    GC::_memory.erase(p); // should call ::operator delete, no recursion
    ::operator delete[](p);
}
<commit_msg>commit<commit_after>// A C++11 toy garbage collector for trivial types using RAII

#include <cstddef>
#include <iostream>
#include <iomanip>
#include <map>
#include <type_traits>
#include <utility>

class GC // RAII Garbage Collector class for PODs
{
    // memory pool
    static std::map<void*, std::pair<std::size_t, bool>> _memory;
public:
    struct collect_t {}; // tag for placement new
    static void display_memory(); // displays the managed memory
    static void clear_memory(); // clears the managed memory
    static bool is_managed(void* p); // true iff p is managed
    static void remove(void *p); // remove p if managed, no-op otherwise
    // perfect candidate for an optional return type
    // returns p if mangaged, (0,false) otherwise
    static std::pair<std::size_t, bool> get(void* p);
    ~GC(); // clears the memory
// friends:
    friend void* operator new(std::size_t size, const GC::collect_t&,
                              bool is_array);
    friend void* operator new[](std::size_t size, const GC::collect_t&);
    friend void operator delete(void *p, const GC::collect_t&) noexcept;
    friend void operator delete[](void *p, const GC::collect_t&) noexcept;
};
std::map<void*, std::pair<std::size_t, bool>> GC::_memory; // ODR

// tagged overloads
void* operator new(std::size_t size, const GC::collect_t&,
                   bool is_array = false);
void* operator new[](std::size_t size, const GC::collect_t&);
void operator delete(void *p, const GC::collect_t&) noexcept;
void operator delete[](void *p, const GC::collect_t&) noexcept;

#define NEW new(GC::collect_t{}) // managed new

struct Foo // a trivial class
{
    int x;
    char buf[256];
};

int main() // Testing
{
    GC gc; // RAII, calls GC::clear_memory() when out-of-scope
    std::cout << "std::is_trivial<Foo>::value: " << std::boolalpha
              << std::is_trivial<Foo>::value << std::endl;

    // use the garbage collector
    char *c = NEW char;
    int *p = NEW int[1024]; // true size: sizeof(int)*100 + y
    Foo *pFoo = NEW Foo;

    // don't use the garbage collector
    int *tmp = new int;

    std::cout << "GC::is_managed(): " << GC::is_managed(c) << std::endl;
    std::cout << "GC::is_managed(): " << GC::is_managed(p) << std::endl;
    std::cout << "GC::is_managed(): " << GC::is_managed(pFoo) << std::endl;
    std::cout << "GC::is_managed(): " << GC::is_managed(tmp) << std::endl;

    GC::display_memory();

    GC::remove(c); // remove it from the pool
    GC::display_memory();
    std::cout << "GC::is_managed(): " << GC::is_managed(c) << std::endl;
    std::cout << "GC::is_managed(): " << GC::is_managed(p) << std::endl;
    std::cout << "GC::is_managed(): " << GC::is_managed(pFoo) << std::endl;
    std::cout << "GC::is_managed(): " << GC::is_managed(tmp) << std::endl;

    auto pair = GC::get(p);
    if (pair.first != 0 && pair.second != false) // element is managed
    {
        std::cout << "GC::get(): " << pair.first
                  << " -> " << pair.second << std::endl;
    }

    delete c;   // the one removed from the pool, so we don't leak memory
    delete tmp; // the un-managed pointer
}

// Implementation

void GC::display_memory()
{
    std::cout << "Allocated: " << std::endl;
    for (auto && elem : _memory)
    {
        std::cout  << std::setfill('.')
                   << "\tADDR: "  << std::setw(8)
                   << elem.first
                   << "\tSIZE: " << std::setw(8) << std::right
                   << elem.second.first
                   << "\tARRAY: " << std::setw(8) << elem.second.second
                   << std::endl;
    }
}

void GC::clear_memory()
{
    for (auto && elem : _memory)
    {
        if (elem.second.second) // non-array
            operator delete(elem.first, GC::collect_t{});
        else
            operator delete[](elem.first, GC::collect_t{});
    }
    _memory.clear();
}

bool GC::is_managed(void* p)
{
    return _memory.find(p) != _memory.end();
}

void GC::remove(void *p)
{
    _memory.erase(p);
}

std::pair<std::size_t, bool> GC::get(void* p)
{
    if (is_managed(p))
        return _memory[p];
    else
        return std::make_pair(0, false);
}

GC::~GC()
{
    std::cout << "GC::~GC() releasing memory" << std::endl;
    clear_memory();
    display_memory();
}

void* operator new(std::size_t size, const GC::collect_t&, bool is_array)
{
    void* addr = ::operator new(size);
    GC::_memory[addr] = std::make_pair(size, is_array);
    return addr;
}

void* operator new[](std::size_t size, const GC::collect_t&)
{
    void* addr = ::operator new[](size);
    GC::_memory[addr] = std::make_pair(size, true);
    return addr;
}

void operator delete(void *p, const GC::collect_t&) noexcept
{
    GC::_memory.erase(p); // should call ::operator delete, no recursion
    ::operator delete(p);
}

void operator delete[](void *p, const GC::collect_t&) noexcept
{
    GC::_memory.erase(p); // should call ::operator delete, no recursion
    ::operator delete[](p);
}
<|endoftext|>