<commit_before>/****************************************************************************
 *
 *   Copyright (c) 2015 PX4 Development Team. All rights reserved.
 *   Author: Mohammed Kabir <mhkabir98@gmail.com>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name PX4 nor the names of its contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 ****************************************************************************/

/**
 * @file camera_trigger.cpp
 *
 * External camera-IMU synchronisation and triggering via FMU auxillary pins.
 *
 * @author Mohammed Kabir <mhkabir98@gmail.com>
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <stdbool.h>
#include <nuttx/clock.h>
#include <nuttx/arch.h>
#include <systemlib/systemlib.h>
#include <systemlib/err.h>
#include <systemlib/param/param.h>
#include <uORB/uORB.h>
#include <uORB/topics/camera_trigger.h>
#include <uORB/topics/sensor_combined.h>
#include <uORB/topics/vehicle_command.h>
#include <poll.h>
#include <drivers/drv_gpio.h>
#include <drivers/drv_hrt.h>
#include <mavlink/mavlink_log.h>
#include <board_config.h>

extern "C" __EXPORT int camera_trigger_main(int argc, char *argv[]);

class CameraTrigger
{
public:
	/**
	 * Constructor
	 */
	CameraTrigger();

	/**
	 * Destructor, also kills task.
	 */
	~CameraTrigger();

	/**
	 * Start the task.
	 */
	void		start();

	/**
	 * Stop the task.
	 */
	void		stop();

	/**
	 * Display info.
	 */
	void		info();

	int 		pin;

private:

	struct hrt_call		_pollcall;
	struct hrt_call		_firecall;

	int 			_gpio_fd;

	int 			_polarity;
	float 			_activation_time;
	float  			_integration_time;
	float  			_transfer_time;
	uint32_t 		_trigger_seq;
	bool	 		_trigger_enabled;

	int			_sensor_sub;
	int			_vcommand_sub;

	orb_advert_t		_trigger_pub;

	struct camera_trigger_s		_trigger;
	struct sensor_combined_s	_sensor;
	struct vehicle_command_s	_command;

	param_t polarity ;
	param_t activation_time ;
	param_t integration_time ;
	param_t transfer_time ;

	/**
	 * Topic poller to check for fire info.
	 */
	static void	poll(void *arg);
	/**
	 * Fires trigger
	 */
	static void	engage(void *arg);
	/**
	 * Resets trigger
	 */
	static void	disengage(void *arg);

};

namespace camera_trigger
{

CameraTrigger	*g_camera_trigger;
}

CameraTrigger::CameraTrigger() :
	pin(1),
	_pollcall{},
	_firecall{},
	_gpio_fd(-1),
	_polarity(0),
	_activation_time(0.0f),
	_integration_time(0.0f),
	_transfer_time(0.0f),
	_trigger_seq(0),
	_trigger_enabled(false),
	_sensor_sub(-1),
	_vcommand_sub(-1),
	_trigger_pub(nullptr),
	_trigger{},
	_sensor{},
	_command{}
{
	memset(&_trigger, 0, sizeof(_trigger));
	memset(&_sensor, 0, sizeof(_sensor));
	memset(&_command, 0, sizeof(_command));

	memset(&_pollcall, 0, sizeof(_pollcall));
	memset(&_firecall, 0, sizeof(_firecall));

	// Parameters
	polarity = param_find("TRIG_POLARITY");
	activation_time = param_find("TRIG_ACT_TIME");
	integration_time = param_find("TRIG_INT_TIME");
	transfer_time = param_find("TRIG_TRANS_TIME");
}

CameraTrigger::~CameraTrigger()
{
	camera_trigger::g_camera_trigger = nullptr;
}

void
CameraTrigger::start()
{

	_sensor_sub = orb_subscribe(ORB_ID(sensor_combined));
	_vcommand_sub = orb_subscribe(ORB_ID(vehicle_command));

	param_get(polarity, &_polarity);
	param_get(activation_time, &_activation_time);
	param_get(integration_time, &_integration_time);
	param_get(transfer_time, &_transfer_time);

	stm32_configgpio(GPIO_GPIO0_OUTPUT);

	if (_polarity == 0) {
		stm32_gpiowrite(GPIO_GPIO0_OUTPUT, 1);	 	// GPIO pin pull high

	} else if (_polarity == 1) {
		stm32_gpiowrite(GPIO_GPIO0_OUTPUT, 0);	 	// GPIO pin pull low

	} else {
		warnx(" invalid trigger polarity setting. stopping.");
		stop();
	}

	poll(this);	// Trampoline call

}

void
CameraTrigger::stop()
{
	hrt_cancel(&_firecall);
	hrt_cancel(&_pollcall);

	if (camera_trigger::g_camera_trigger != nullptr) {
		delete(camera_trigger::g_camera_trigger);
	}
}

void
CameraTrigger::poll(void *arg)
{

	CameraTrigger *trig = reinterpret_cast<CameraTrigger *>(arg);

	bool updated;
	orb_check(trig->_vcommand_sub, &updated);

	if (updated) {

		orb_copy(ORB_ID(vehicle_command), trig->_vcommand_sub, &trig->_command);

		if (trig->_command.command == vehicle_command_s::VEHICLE_CMD_DO_TRIGGER_CONTROL) {
			if (trig->_command.param1 < 1) {
				if (trig->_trigger_enabled) {
					trig->_trigger_enabled = false ;
				}

			} else if (trig->_command.param1 >= 1) {
				if (!trig->_trigger_enabled) {
					trig->_trigger_enabled = true ;
				}
			}

			// Set trigger rate from command
			if (trig->_command.param2 > 0) {
				trig->_integration_time = trig->_command.param2;
				param_set(trig->integration_time, &(trig->_integration_time));
			}
		}
	}

	if (!trig->_trigger_enabled)	{
		hrt_call_after(&trig->_pollcall, 1e6, (hrt_callout)&CameraTrigger::poll, trig);
		return;

	} else {
		engage(trig);
		hrt_call_after(&trig->_firecall, trig->_activation_time * 1000, (hrt_callout)&CameraTrigger::disengage, trig);

		orb_copy(ORB_ID(sensor_combined), trig->_sensor_sub, &trig->_sensor);

		trig->_trigger.timestamp = trig->_sensor.timestamp;	// get IMU timestamp
		trig->_trigger.seq = trig->_trigger_seq++;

		if (trig->_trigger_pub != nullptr) {
			orb_publish(ORB_ID(camera_trigger), trig->_trigger_pub, &trig->_trigger);

		} else {
			trig->_trigger_pub = orb_advertise(ORB_ID(camera_trigger), &trig->_trigger);
		}

		hrt_call_after(&trig->_pollcall, (trig->_transfer_time + trig->_integration_time) * 1000,
			       (hrt_callout)&CameraTrigger::poll, trig);
	}

}

void
CameraTrigger::engage(void *arg)
{

	CameraTrigger *trig = reinterpret_cast<CameraTrigger *>(arg);

	stm32_configgpio(GPIO_GPIO0_OUTPUT);

	if (trig->_polarity == 0) {	// ACTIVE_LOW
		stm32_gpiowrite(GPIO_GPIO0_OUTPUT, 0);

	} else if (trig->_polarity == 1) {	// ACTIVE_HIGH
		stm32_gpiowrite(GPIO_GPIO0_OUTPUT, 1);
	}

}

void
CameraTrigger::disengage(void *arg)
{

	CameraTrigger *trig = reinterpret_cast<CameraTrigger *>(arg);

	stm32_configgpio(GPIO_GPIO0_OUTPUT);

	if (trig->_polarity == 0) {	// ACTIVE_LOW
		stm32_gpiowrite(GPIO_GPIO0_OUTPUT, 1);

	} else if (trig->_polarity == 1) {	// ACTIVE_HIGH
		stm32_gpiowrite(GPIO_GPIO0_OUTPUT, 0);
	}

}

void
CameraTrigger::info()
{
	warnx("Trigger state : %s", _trigger_enabled ? "enabled" : "disabled");
	warnx("Trigger pin : %i", pin);
	warnx("Trigger polarity : %s", _polarity ? "ACTIVE_HIGH" : "ACTIVE_LOW");
	warnx("Shutter integration time : %.2f", (double)_integration_time);
}

static void usage()
{
	errx(1, "usage: camera_trigger {start|stop|info} [-p <n>]\n"
	     "\t-p <n>\tUse specified AUX OUT pin number (default: 1)"
	    );
}

int camera_trigger_main(int argc, char *argv[])
{
	if (argc < 2) {
		usage();
	}

	if (!strcmp(argv[1], "start")) {

		if (camera_trigger::g_camera_trigger != nullptr) {
			errx(0, "already running");
		}

		camera_trigger::g_camera_trigger = new CameraTrigger;

		if (camera_trigger::g_camera_trigger == nullptr) {
			errx(1, "alloc failed");
		}

		if (argc > 3) {

			camera_trigger::g_camera_trigger->pin = (int)argv[3];

			if (atoi(argv[3]) > 0 && atoi(argv[3]) < 6) {
				warnx("starting trigger on pin : %li ", atoi(argv[3]));
				camera_trigger::g_camera_trigger->pin = atoi(argv[3]);

			} else {
				usage();
			}
		}

		camera_trigger::g_camera_trigger->start();

		return 0;
	}

	if (camera_trigger::g_camera_trigger == nullptr) {
		errx(1, "not running");
	}

	else if (!strcmp(argv[1], "stop")) {
		camera_trigger::g_camera_trigger->stop();

	} else if (!strcmp(argv[1], "info")) {
		camera_trigger::g_camera_trigger->info();

	} else {
		usage();
	}

	return 0;
}

<commit_msg>camera trigger : multipin support<commit_after>/****************************************************************************
 *
 *   Copyright (c) 2015 PX4 Development Team. All rights reserved.
 *   Author: Mohammed Kabir <mhkabir98@gmail.com>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name PX4 nor the names of its contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 ****************************************************************************/

/**
 * @file camera_trigger.cpp
 *
 * External camera-IMU synchronisation and triggering via FMU auxillary pins.
 *
 * @author Mohammed Kabir <mhkabir98@gmail.com>
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <stdbool.h>
#include <nuttx/clock.h>
#include <nuttx/arch.h>
#include <systemlib/systemlib.h>
#include <systemlib/err.h>
#include <systemlib/param/param.h>
#include <uORB/uORB.h>
#include <uORB/topics/camera_trigger.h>
#include <uORB/topics/sensor_combined.h>
#include <uORB/topics/vehicle_command.h>
#include <poll.h>
#include <drivers/drv_gpio.h>
#include <drivers/drv_hrt.h>
#include <mavlink/mavlink_log.h>
#include <board_config.h>

extern "C" __EXPORT int camera_trigger_main(int argc, char *argv[]);

class CameraTrigger
{
public:
	/**
	 * Constructor
	 */
	CameraTrigger();

	/**
	 * Destructor, also kills task.
	 */
	~CameraTrigger();

	/**
	 * Start the task.
	 */
	void		start();

	/**
	 * Stop the task.
	 */
	void		stop();

	/**
	 * Display info.
	 */
	void		info();

	int 		pin;

private:

	struct hrt_call		_pollcall;
	struct hrt_call		_firecall;

	int 			_gpio_fd;

	int 			_polarity;
	float 			_activation_time;
	float  			_integration_time;
	float  			_transfer_time;
	uint32_t 		_trigger_seq;
	bool	 		_trigger_enabled;

	int			_sensor_sub;
	int			_vcommand_sub;

	orb_advert_t		_trigger_pub;

	struct camera_trigger_s		_trigger;
	struct sensor_combined_s	_sensor;
	struct vehicle_command_s	_command;

	param_t polarity ;
	param_t activation_time ;
	param_t integration_time ;
	param_t transfer_time ;

	int32_t _gpios[6] = {
		GPIO_GPIO0_OUTPUT,
		GPIO_GPIO1_OUTPUT,
		GPIO_GPIO2_OUTPUT,
		GPIO_GPIO3_OUTPUT,
		GPIO_GPIO4_OUTPUT,
		GPIO_GPIO5_OUTPUT
	};

	/**
	 * Topic poller to check for fire info.
	 */
	static void	poll(void *arg);
	/**
	 * Fires trigger
	 */
	static void	engage(void *arg);
	/**
	 * Resets trigger
	 */
	static void	disengage(void *arg);

};

namespace camera_trigger
{

CameraTrigger	*g_camera_trigger;
}

CameraTrigger::CameraTrigger() :
	pin(1),
	_pollcall {},
	_firecall {},
	_gpio_fd(-1),
	_polarity(0),
	_activation_time(0.0f),
	_integration_time(0.0f),
	_transfer_time(0.0f),
	_trigger_seq(0),
	_trigger_enabled(false),
	_sensor_sub(-1),
	_vcommand_sub(-1),
	_trigger_pub(nullptr),
	_trigger {},
	_sensor {},
	_command {},
	_gpios{} {

	memset(&_pollcall, 0, sizeof(_pollcall));
	memset(&_firecall, 0, sizeof(_firecall));

	memset(&_trigger, 0, sizeof(_trigger));
	memset(&_sensor, 0, sizeof(_sensor));
	memset(&_command, 0, sizeof(_command));

	memset(&_gpios, 0, sizeof(_gpios));

	// Parameters
	polarity = param_find("TRIG_POLARITY");
	activation_time = param_find("TRIG_ACT_TIME");
	integration_time = param_find("TRIG_INT_TIME");
	transfer_time = param_find("TRIG_TRANS_TIME");
}

CameraTrigger::~CameraTrigger()
{
	camera_trigger::g_camera_trigger = nullptr;
}

void
CameraTrigger::start()
{

	_sensor_sub = orb_subscribe(ORB_ID(sensor_combined));
	_vcommand_sub = orb_subscribe(ORB_ID(vehicle_command));

	param_get(polarity, &_polarity);
	param_get(activation_time, &_activation_time);
	param_get(integration_time, &_integration_time);
	param_get(transfer_time, &_transfer_time);

	stm32_configgpio(_gpios[pin - 1]);

	if (_polarity == 0) {
		stm32_gpiowrite(_gpios[pin - 1], 1);	 	// GPIO pin pull high

	} else if (_polarity == 1) {
		stm32_gpiowrite(_gpios[pin - 1], 0);	 	// GPIO pin pull low

	} else {
		warnx(" invalid trigger polarity setting. stopping.");
		stop();
	}

	poll(this);	// Trampoline call

}

void
CameraTrigger::stop()
{
	hrt_cancel(&_firecall);
	hrt_cancel(&_pollcall);

	if (camera_trigger::g_camera_trigger != nullptr) {
		delete(camera_trigger::g_camera_trigger);
	}
}

void
CameraTrigger::poll(void *arg)
{

	CameraTrigger *trig = reinterpret_cast<CameraTrigger *>(arg);

	bool updated;
	orb_check(trig->_vcommand_sub, &updated);

	if (updated) {

		orb_copy(ORB_ID(vehicle_command), trig->_vcommand_sub, &trig->_command);

		if (trig->_command.command == vehicle_command_s::VEHICLE_CMD_DO_TRIGGER_CONTROL) {
			if (trig->_command.param1 < 1) {
				if (trig->_trigger_enabled) {
					trig->_trigger_enabled = false ;
				}

			} else if (trig->_command.param1 >= 1) {
				if (!trig->_trigger_enabled) {
					trig->_trigger_enabled = true ;
				}
			}

			// Set trigger rate from command
			if (trig->_command.param2 > 0) {
				trig->_integration_time = trig->_command.param2;
				param_set(trig->integration_time, &(trig->_integration_time));
			}
		}
	}

	if (!trig->_trigger_enabled)	{
		hrt_call_after(&trig->_pollcall, 1e6, (hrt_callout)&CameraTrigger::poll, trig);
		return;

	} else {
		engage(trig);
		hrt_call_after(&trig->_firecall, trig->_activation_time * 1000, (hrt_callout)&CameraTrigger::disengage, trig);

		orb_copy(ORB_ID(sensor_combined), trig->_sensor_sub, &trig->_sensor);

		trig->_trigger.timestamp = trig->_sensor.timestamp;	// get IMU timestamp
		trig->_trigger.seq = trig->_trigger_seq++;

		if (trig->_trigger_pub != nullptr) {
			orb_publish(ORB_ID(camera_trigger), trig->_trigger_pub, &trig->_trigger);

		} else {
			trig->_trigger_pub = orb_advertise(ORB_ID(camera_trigger), &trig->_trigger);
		}

		hrt_call_after(&trig->_pollcall, (trig->_transfer_time + trig->_integration_time) * 1000,
			       (hrt_callout)&CameraTrigger::poll, trig);
	}

}

void
CameraTrigger::engage(void *arg)
{

	CameraTrigger *trig = reinterpret_cast<CameraTrigger *>(arg);

	stm32_configgpio(trig->_gpios[trig->pin - 1]);

	if (trig->_polarity == 0) {	// ACTIVE_LOW
		stm32_gpiowrite(trig->_gpios[trig->pin - 1], 0);

	} else if (trig->_polarity == 1) {	// ACTIVE_HIGH
		stm32_gpiowrite(trig->_gpios[trig->pin - 1], 1);
	}

}

void
CameraTrigger::disengage(void *arg)
{

	CameraTrigger *trig = reinterpret_cast<CameraTrigger *>(arg);

	stm32_configgpio(trig->_gpios[trig->pin - 1]);

	if (trig->_polarity == 0) {		// ACTIVE_LOW
		stm32_gpiowrite(trig->_gpios[trig->pin - 1], 1);

	} else if (trig->_polarity == 1) {	// ACTIVE_HIGH
		stm32_gpiowrite(trig->_gpios[trig->pin - 1], 0);
	}

}

void
CameraTrigger::info()
{
	warnx("Trigger state : %s", _trigger_enabled ? "enabled" : "disabled");
	warnx("Trigger pin : %i", pin);
	warnx("Trigger polarity : %s", _polarity ? "ACTIVE_HIGH" : "ACTIVE_LOW");
	warnx("Shutter integration time : %.2f", (double)_integration_time);
}

static void usage()
{
	errx(1, "usage: camera_trigger {start|stop|info} [-p <n>]\n"
	     "\t-p <n>\tUse specified AUX OUT pin number (default: 1)"
	    );
}

int camera_trigger_main(int argc, char *argv[])
{
	if (argc < 2) {
		usage();
	}

	if (!strcmp(argv[1], "start")) {

		if (camera_trigger::g_camera_trigger != nullptr) {
			errx(0, "already running");
		}

		camera_trigger::g_camera_trigger = new CameraTrigger;

		if (camera_trigger::g_camera_trigger == nullptr) {
			errx(1, "alloc failed");
		}

		if (argc > 3) {

			camera_trigger::g_camera_trigger->pin = (int)argv[3];

			if (atoi(argv[3]) > 0 && atoi(argv[3]) < 6) {
				warnx("starting trigger on pin : %li ", atoi(argv[3]));
				camera_trigger::g_camera_trigger->pin = atoi(argv[3]);

			} else {
				usage();
			}
		}

		camera_trigger::g_camera_trigger->start();

		return 0;
	}

	if (camera_trigger::g_camera_trigger == nullptr) {
		errx(1, "not running");
	}

	else if (!strcmp(argv[1], "stop")) {
		camera_trigger::g_camera_trigger->stop();

	} else if (!strcmp(argv[1], "info")) {
		camera_trigger::g_camera_trigger->info();

	} else {
		usage();
	}

	return 0;
}

<|endoftext|>


<commit_before>#include <iostream>

template<class T>
struct type_to_string;

template<>
struct type_to_string<char>
{
  static constexpr const char *get(){return "char";};
};
template<>
struct type_to_string<short int>
{
  static constexpr const char *get(){return "short int";};
};
template<>
struct type_to_string<int>
{
  static constexpr const char *get(){return "int";};
};
template<>
struct type_to_string<long int>
{
  static constexpr const char *get(){return "long int";}
};

struct Const
{
  static constexpr const char* get()
  {
    return " const";
  }
};
struct Volatile
{
  static constexpr const char* get()
  {
    return " volatile";
  }
};
struct Pointer
{
  static constexpr const char* get()
  {
    return "*";
  }
};
struct LValRef
{
  static constexpr const char* get()
  {
    return "&";
  }
};
struct RValRef
{
  static constexpr const char* get()
  {
    return "&&";
  }
};

template<char... Cs>
struct metastring {};

template<char... Cs>
std::ostream& operator<<(std::ostream& os, metastring<Cs...>)
{
  const char data[sizeof...(Cs)] = {Cs...};
  os << data;
  return os;
}

template<size_t N, char... Cs>
struct number_string
{
  using type = typename number_string<N/10, '0'+(N%10), Cs...>::type;
};
template<char... Cs>
struct number_string<0,Cs...>
{
  using type = metastring<'*','[',Cs...,']'>;
};

template<size_t N>
struct Array
{
  using metastring = typename number_string<N>::type;
  static constexpr metastring get()
  {
    return metastring{};
  }
};

template<class...>
struct QualPrinter
{
  friend void operator<<(std::ostream &os, const QualPrinter &) {}
};

template<class Qual, class... Quals>
struct QualPrinter<Qual, Quals...>
{
  friend std::ostream& operator<<(std::ostream& os, const QualPrinter&)
  {
    os << Qual::get()<< QualPrinter<Quals...>();
    return os;
  }
};

template<class Type, class... Quals>
struct type_descriptor
{
  friend std::ostream& operator<<(std::ostream& os, const type_descriptor&)
  {
    os << type_to_string<Type>::get() << QualPrinter<Quals...>();
    return os;
  }
};

template <class T, class... Quals>
struct type_descriptor<T const, Quals...> : type_descriptor<T, Const, Quals...> {};

template <class T, class... Quals>
struct type_descriptor<T volatile, Quals...> : type_descriptor<T, Volatile, Quals...> {};

template <class T, class... Quals>
struct type_descriptor<T *, Quals...> : type_descriptor<T, Pointer, Quals...> {};

template <class T, class... Quals>
struct type_descriptor<T &, Quals...> : type_descriptor<T, LValRef, Quals...> {};

template <class T, class... Quals>
struct type_descriptor<T &&, Quals...> : type_descriptor<T, RValRef, Quals...> {};
template <class T, size_t N, class... Quals>
struct type_descriptor<T *[N], Quals...> : type_descriptor<T, Array<N>, Quals...> {};

int main()
{
  using namespace std;

  cout << type_descriptor<int>() << endl;
  cout << type_descriptor<long int>() << endl;
  cout << type_descriptor<char >() << endl;
  cout << type_descriptor<short >() << endl;
  cout << type_descriptor<int const*& >() << endl;
  cout << type_descriptor<int const*&& >() << endl;
  cout << type_descriptor<int const* volatile >() << endl;
  cout << type_descriptor<int * volatile >() << endl;
  cout << type_descriptor<long const* volatile>() << endl;
  cout << type_descriptor<long const*[29]>() << endl;
}
<commit_msg>put << operator inside metastring<commit_after>#include <iostream>

template<class T>
struct type_to_string;

template<>
struct type_to_string<char>
{
  static constexpr const char *get(){return "char";};
};
template<>
struct type_to_string<short int>
{
  static constexpr const char *get(){return "short int";};
};
template<>
struct type_to_string<int>
{
  static constexpr const char *get(){return "int";};
};
template<>
struct type_to_string<long int>
{
  static constexpr const char *get(){return "long int";}
};

struct Const
{
  static constexpr const char* get()
  {
    return " const";
  }
};
struct Volatile
{
  static constexpr const char* get()
  {
    return " volatile";
  }
};
struct Pointer
{
  static constexpr const char* get()
  {
    return "*";
  }
};
struct LValRef
{
  static constexpr const char* get()
  {
    return "&";
  }
};
struct RValRef
{
  static constexpr const char* get()
  {
    return "&&";
  }
};

template<char... Cs>
struct metastring
{
  friend std::ostream &operator<<(std::ostream &os, metastring)
  {
    const char data[sizeof...(Cs)] = {Cs...};
    os << data;
    return os;
  }
};

template<size_t N, char... Cs>
struct number_string
{
  using type = typename number_string<N/10, '0'+(N%10), Cs...>::type;
};
template<char... Cs>
struct number_string<0,Cs...>
{
  using type = metastring<'*','[',Cs...,']'>;
};

template<size_t N>
struct Array
{
  using metastring = typename number_string<N>::type;
  static const metastring get()
  {
    return metastring{};
  }
};

template<class...>
struct QualPrinter
{
  friend void operator<<(std::ostream &os, const QualPrinter &) {}
};

template<class Qual, class... Quals>
struct QualPrinter<Qual, Quals...>
{
  friend std::ostream& operator<<(std::ostream& os, const QualPrinter&)
  {
    os << Qual::get()<< QualPrinter<Quals...>();
    return os;
  }
};

template<class Type, class... Quals>
struct type_descriptor
{
  friend std::ostream& operator<<(std::ostream& os, const type_descriptor&)
  {
    os << type_to_string<Type>::get() << QualPrinter<Quals...>();
    return os;
  }
};

template <class T, class... Quals>
struct type_descriptor<T const, Quals...> : type_descriptor<T, Const, Quals...> {};

template <class T, class... Quals>
struct type_descriptor<T volatile, Quals...> : type_descriptor<T, Volatile, Quals...> {};

template <class T, class... Quals>
struct type_descriptor<T *, Quals...> : type_descriptor<T, Pointer, Quals...> {};

template <class T, class... Quals>
struct type_descriptor<T &, Quals...> : type_descriptor<T, LValRef, Quals...> {};

template <class T, class... Quals>
struct type_descriptor<T &&, Quals...> : type_descriptor<T, RValRef, Quals...> {};
template <class T, size_t N, class... Quals>
struct type_descriptor<T *[N], Quals...> : type_descriptor<T, Array<N>, Quals...> {};

int main()
{
  using namespace std;

  cout << type_descriptor<int>() << endl;
  cout << type_descriptor<long int>() << endl;
  cout << type_descriptor<char >() << endl;
  cout << type_descriptor<short >() << endl;
  cout << type_descriptor<int const*& >() << endl;
  cout << type_descriptor<int const*&& >() << endl;
  cout << type_descriptor<int const* volatile >() << endl;
  cout << type_descriptor<int * volatile >() << endl;
  cout << type_descriptor<long const* volatile>() << endl;
  cout << type_descriptor<long const*[29]>() << endl;
}
<|endoftext|>


<commit_before>#include <QString>
#include <QtTest>

#include <libgpsbip/OptionTypes.h>
#include <libgpsbip/OptionsGroupBase.h>

class MockGroup : public gpsbip::OptionsGroupBase
{
    Q_OBJECT

public:
    MockGroup() {
        auto r = new gpsbip::BoolOption();
        root = addOption(r);
        opt = addOption<gpsbip::BoolOption>("label");
        strOpt = addOption<gpsbip::StringOption>(*r, "label");
    }

    gpsbip::BoolOption* getRootOption() { return &get<gpsbip::BoolOption>(root); }
    gpsbip::BoolOption* getOption() { return &get<gpsbip::BoolOption>(opt); }
    gpsbip::StringOption* getStrOption() { return &get<gpsbip::StringOption>(strOpt); }

private:
    int root, opt, strOpt;
};

class Tst_optionsGroups : public QObject
{
    Q_OBJECT

public:
    Tst_optionsGroups();

private Q_SLOTS:
    void testOptionGroupMechanic();
};

Tst_optionsGroups::Tst_optionsGroups()
{
}

void Tst_optionsGroups::testOptionGroupMechanic()
{
    // Validates all 3 addOption variants
    MockGroup g;

    // Validates get<>
    gpsbip::BoolOption *root = g.getRootOption();
    QVERIFY(root != nullptr);
    gpsbip::BoolOption *opt = g.getOption();
    QVERIFY(opt != nullptr);
}

QTEST_APPLESS_MAIN(Tst_optionsGroups)

#include "tst_optionsgroups.moc"
<commit_msg>More base group checking (forgot to refresh before committing previous changes, sorry!)<commit_after>#include <QString>
#include <QtTest>

#include <libgpsbip/OptionTypes.h>
#include <libgpsbip/OptionsGroupBase.h>

class MockGroup : public gpsbip::OptionsGroupBase
{
    Q_OBJECT

public:
    MockGroup() {
        auto r = new gpsbip::BoolOption();
        root = addOption(r);
        opt = addOption<gpsbip::BoolOption>("label");
        strOpt = addOption<gpsbip::StringOption>(*r, "label");
    }

    gpsbip::BoolOption* getRootOption() { return &get<gpsbip::BoolOption>(root); }
    gpsbip::BoolOption* getOption() { return &get<gpsbip::BoolOption>(opt); }
    gpsbip::StringOption* getStrOption() { return &get<gpsbip::StringOption>(strOpt); }

private:
    int root, opt, strOpt;
};

class Tst_optionsGroups : public QObject
{
    Q_OBJECT

public:
    Tst_optionsGroups();

private Q_SLOTS:
    void testOptionGroupMechanic();
};

Tst_optionsGroups::Tst_optionsGroups()
{
}

void Tst_optionsGroups::testOptionGroupMechanic()
{
    try {
        // Validates all 3 addOption variants (by definition, can't really assert that :/)
        MockGroup g;

        // Validates get<>
        gpsbip::BoolOption *root = g.getRootOption();
        QVERIFY(root != nullptr);
        gpsbip::BoolOption *opt = g.getOption();
        QVERIFY(opt != nullptr);
        gpsbip::StringOption *str = g.getStrOption();
        QVERIFY(str != nullptr);

        // Confirm that the string option is dependant on the root one and that the expected addOption was used
        QCOMPARE(str->isEnabled(), false);
        *root = true;
        QCOMPARE(str->isEnabled(), true);
    }
    catch (std::exception &e) {
        QFAIL(e.what());
    }
}

QTEST_APPLESS_MAIN(Tst_optionsGroups)

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


<commit_before>/**
 * acljobs.cpp
 *
 * Copyright (c) 2004 David Faure <faure@kde.org>
 *
 *
 *  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; version 2 of the License
 *
 *  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.
 *
 *  In addition, as a special exception, the copyright holders give
 *  permission to link the code of this program with any edition of
 *  the Qt library by Trolltech AS, Norway (or with modified versions
 *  of Qt that use the same license as Qt), and distribute linked
 *  combinations including the two.  You must obey the GNU General
 *  Public License in all respects for all of the code used other than
 *  Qt.  If you modify this file, you may extend this exception to
 *  your version of the file, but you are not obligated to do so.  If
 *  you do not wish to do so, delete this exception statement from
 *  your version.
 */
#include "acljobs.h"
#include <kio/scheduler.h>
#include <kdebug.h>

using namespace KMail;

// Convert str to an ACLPermissions value.
// url and user are there only for the error message
static unsigned int IMAPRightsToPermission( const QString& str, const KURL& url, const QString& user ) {
  unsigned int perm = 0;
  bool foundSeenPerm = false;
  uint len = str.length();
  for (uint i = 0; i < len; ++i) {
    QChar ch = str[i];
    switch ( ch.latin1() ) {
    case 'l': perm |= ACLJobs::List; break;
    case 'r': perm |= ACLJobs::Read; break;
    case 's': foundSeenPerm = true; break;
    case 'w': perm |= ACLJobs::WriteFlags; break;
    case 'i': perm |= ACLJobs::Insert; break;
    case 'p': perm |= ACLJobs::Post; break;
    case 'c': perm |= ACLJobs::Create; break;
    case 'd': perm |= ACLJobs::Delete; break;
    case 'a': perm |= ACLJobs::Administer; break;
    default: break;
    }
  }
  if ( ( perm & ACLJobs::Read ) && str.find( 's' ) == -1 ) {
    // Reading without 'seen' is, well, annoying. Unusable, even.
    // So we treat 'rs' as a single one.
    // But if the permissions were set out of kmail, better check that both are set
    kdWarning(5006) << "IMAPRightsToPermission: found read (r) but not seen (s). Things will not work well for folder " << url << " and user " << ( user.isEmpty() ? "myself" : user ) << endl;
    if ( perm & ACLJobs::Administer )
      kdWarning(5006) << "You can change this yourself in the ACL dialog" << endl;
    else
      kdWarning(5006) << "Ask your admin for 's' permissions." << endl;
    // Is the above correct enough to be turned into a KMessageBox?
  }

  return perm;
}

static QCString permissionsToIMAPRights( unsigned int permissions ) {
  QCString str = "";
  if ( permissions & ACLJobs::List )
    str += 'l';
  if ( permissions & ACLJobs::Read )
    str += "rs";
  if ( permissions & ACLJobs::WriteFlags )
    str += 'w';
  if ( permissions & ACLJobs::Insert )
    str += 'i';
  if ( permissions & ACLJobs::Post )
    str += 'p';
  if ( permissions & ACLJobs::Create )
    str += 'c';
  if ( permissions & ACLJobs::Delete )
    str += 'd';
  if ( permissions & ACLJobs::Administer )
    str += 'a';
  return str;
}

#ifndef NDEBUG
QString ACLJobs::permissionsToString( unsigned int permissions )
{
  QString str;
  if ( permissions & ACLJobs::List )
    str += "List ";
  if ( permissions & ACLJobs::Read )
    str += "Read ";
  if ( permissions & ACLJobs::WriteFlags )
    str += "Write ";
  if ( permissions & ACLJobs::Insert )
    str += "Insert ";
  if ( permissions & ACLJobs::Post )
    str += "Post ";
  if ( permissions & ACLJobs::Create )
    str += "Create ";
  if ( permissions & ACLJobs::Delete )
    str += "Delete ";
  if ( permissions & ACLJobs::Administer )
    str += "Administer ";
  if ( !str.isEmpty() )
    str.truncate( str.length() - 1 );
  return str;
}
#endif

KIO::SimpleJob* ACLJobs::setACL( KIO::Slave* slave, const KURL& url, const QString& user, unsigned int permissions )
{
  QString perm = QString::fromLatin1( permissionsToIMAPRights( permissions ) );

  QByteArray packedArgs;
  QDataStream stream( packedArgs, IO_WriteOnly );
  stream << (int)'A' << (int)'S' << url << user << perm;

  KIO::SimpleJob* job = KIO::special( url, packedArgs, false );
  KIO::Scheduler::assignJobToSlave( slave, job );
  return job;
}

ACLJobs::DeleteACLJob* ACLJobs::deleteACL( KIO::Slave* slave, const KURL& url, const QString& user )
{
  QByteArray packedArgs;
  QDataStream stream( packedArgs, IO_WriteOnly );
  stream << (int)'A' << (int)'D' << url << user;

  ACLJobs::DeleteACLJob* job = new ACLJobs::DeleteACLJob( url, user, packedArgs, false );
  KIO::Scheduler::assignJobToSlave( slave, job );
  return job;
}

ACLJobs::GetACLJob* ACLJobs::getACL( KIO::Slave* slave, const KURL& url )
{
  QByteArray packedArgs;
  QDataStream stream( packedArgs, IO_WriteOnly );
  stream << (int)'A' << (int)'G' << url;

  ACLJobs::GetACLJob* job = new ACLJobs::GetACLJob( url, packedArgs, false );
  KIO::Scheduler::assignJobToSlave( slave, job );
  return job;
}

ACLJobs::GetUserRightsJob* ACLJobs::getUserRights( KIO::Slave* slave, const KURL& url )
{
  QByteArray packedArgs;
  QDataStream stream( packedArgs, IO_WriteOnly );
  stream << (int)'A' << (int)'M' << url;

  ACLJobs::GetUserRightsJob* job = new ACLJobs::GetUserRightsJob( url, packedArgs, false );
  KIO::Scheduler::assignJobToSlave( slave, job );
  return job;
}

ACLJobs::GetACLJob::GetACLJob( const KURL& url, const QByteArray &packedArgs,
                                 bool showProgressInfo )
  : KIO::SimpleJob( url, KIO::CMD_SPECIAL, packedArgs, showProgressInfo )
{
  connect( this, SIGNAL(infoMessage(KIO::Job*,const QString&)),
           SLOT(slotInfoMessage(KIO::Job*,const QString&)) );
}

void ACLJobs::GetACLJob::slotInfoMessage( KIO::Job*, const QString& str )
{
  // Parse the result
  QStringList lst = QStringList::split( " ", str );
  while ( lst.count() >= 2 ) // we take items 2 by 2
  {
    QString user = lst.front(); lst.pop_front();
    QString imapRights = lst.front(); lst.pop_front();
    unsigned int perm = IMAPRightsToPermission( imapRights, url(), user );
    m_entries.append( ACLListEntry( user, imapRights, perm ) );
  }
}

ACLJobs::GetUserRightsJob::GetUserRightsJob( const KURL& url, const QByteArray &packedArgs,
                                               bool showProgressInfo )
  : KIO::SimpleJob( url, KIO::CMD_SPECIAL, packedArgs, showProgressInfo )
{
  connect( this, SIGNAL(infoMessage(KIO::Job*,const QString&)),
           SLOT(slotInfoMessage(KIO::Job*,const QString&)) );
}

void ACLJobs::GetUserRightsJob::slotInfoMessage( KIO::Job*, const QString& str )
{
  // Parse the result
  m_permissions = IMAPRightsToPermission( str, url(), QString::null );
}

ACLJobs::DeleteACLJob::DeleteACLJob( const KURL& url, const QString& userId,
                                     const QByteArray &packedArgs,
                                     bool showProgressInfo )
  : KIO::SimpleJob( url, KIO::CMD_SPECIAL, packedArgs, showProgressInfo ),
    mUserId( userId )
{
}

////

ACLJobs::MultiSetACLJob::MultiSetACLJob( KIO::Slave* slave, const KURL& url, const ACLList& acl, bool showProgressInfo )
  : KIO::Job( showProgressInfo ),
    mSlave( slave ),
    mUrl( url ), mACLList( acl ), mACLListIterator( mACLList.begin() )
{
  QTimer::singleShot(0, this, SLOT(slotStart()));
}

void ACLJobs::MultiSetACLJob::slotStart()
{
  // Skip over unchanged entries
  while ( mACLListIterator != mACLList.end() && !(*mACLListIterator).changed )
    ++mACLListIterator;

  if ( mACLListIterator != mACLList.end() )
  {
    const ACLListEntry& entry = *mACLListIterator;
    KIO::Job* job = 0;
    if ( entry.permissions > -1 )
      job = setACL( mSlave, mUrl, entry.userId, entry.permissions );
    else
      job = deleteACL( mSlave, mUrl, entry.userId );

    addSubjob( job );
  } else { // done!
    emitResult();
  }
}

void ACLJobs::MultiSetACLJob::slotResult( KIO::Job *job )
{
  if ( job->error() ) {
    KIO::Job::slotResult( job ); // will set the error and emit result(this)
    return;
  }
  subjobs.remove(job);
  const ACLListEntry& entry = *mACLListIterator;
  emit aclChanged( entry.userId, entry.permissions );

  // Move on to next one
  ++mACLListIterator;
  slotStart();
}

ACLJobs::MultiSetACLJob* ACLJobs::multiSetACL( KIO::Slave* slave, const KURL& url, const ACLList& acl )
{
  return new MultiSetACLJob( slave, url, acl, false /*showProgressInfo*/ );
}

#include "acljobs.moc"
<commit_msg>Don't drop the empty fields of the ACL description.<commit_after>/**
 * acljobs.cpp
 *
 * Copyright (c) 2004 David Faure <faure@kde.org>
 *
 *
 *  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; version 2 of the License
 *
 *  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.
 *
 *  In addition, as a special exception, the copyright holders give
 *  permission to link the code of this program with any edition of
 *  the Qt library by Trolltech AS, Norway (or with modified versions
 *  of Qt that use the same license as Qt), and distribute linked
 *  combinations including the two.  You must obey the GNU General
 *  Public License in all respects for all of the code used other than
 *  Qt.  If you modify this file, you may extend this exception to
 *  your version of the file, but you are not obligated to do so.  If
 *  you do not wish to do so, delete this exception statement from
 *  your version.
 */
#include "acljobs.h"
#include <kio/scheduler.h>
#include <kdebug.h>

using namespace KMail;

// Convert str to an ACLPermissions value.
// url and user are there only for the error message
static unsigned int IMAPRightsToPermission( const QString& str, const KURL& url, const QString& user ) {
  unsigned int perm = 0;
  bool foundSeenPerm = false;
  uint len = str.length();
  for (uint i = 0; i < len; ++i) {
    QChar ch = str[i];
    switch ( ch.latin1() ) {
    case 'l': perm |= ACLJobs::List; break;
    case 'r': perm |= ACLJobs::Read; break;
    case 's': foundSeenPerm = true; break;
    case 'w': perm |= ACLJobs::WriteFlags; break;
    case 'i': perm |= ACLJobs::Insert; break;
    case 'p': perm |= ACLJobs::Post; break;
    case 'c': perm |= ACLJobs::Create; break;
    case 'd': perm |= ACLJobs::Delete; break;
    case 'a': perm |= ACLJobs::Administer; break;
    default: break;
    }
  }
  if ( ( perm & ACLJobs::Read ) && str.find( 's' ) == -1 ) {
    // Reading without 'seen' is, well, annoying. Unusable, even.
    // So we treat 'rs' as a single one.
    // But if the permissions were set out of kmail, better check that both are set
    kdWarning(5006) << "IMAPRightsToPermission: found read (r) but not seen (s). Things will not work well for folder " << url << " and user " << ( user.isEmpty() ? "myself" : user ) << endl;
    if ( perm & ACLJobs::Administer )
      kdWarning(5006) << "You can change this yourself in the ACL dialog" << endl;
    else
      kdWarning(5006) << "Ask your admin for 's' permissions." << endl;
    // Is the above correct enough to be turned into a KMessageBox?
  }

  return perm;
}

static QCString permissionsToIMAPRights( unsigned int permissions ) {
  QCString str = "";
  if ( permissions & ACLJobs::List )
    str += 'l';
  if ( permissions & ACLJobs::Read )
    str += "rs";
  if ( permissions & ACLJobs::WriteFlags )
    str += 'w';
  if ( permissions & ACLJobs::Insert )
    str += 'i';
  if ( permissions & ACLJobs::Post )
    str += 'p';
  if ( permissions & ACLJobs::Create )
    str += 'c';
  if ( permissions & ACLJobs::Delete )
    str += 'd';
  if ( permissions & ACLJobs::Administer )
    str += 'a';
  return str;
}

#ifndef NDEBUG
QString ACLJobs::permissionsToString( unsigned int permissions )
{
  QString str;
  if ( permissions & ACLJobs::List )
    str += "List ";
  if ( permissions & ACLJobs::Read )
    str += "Read ";
  if ( permissions & ACLJobs::WriteFlags )
    str += "Write ";
  if ( permissions & ACLJobs::Insert )
    str += "Insert ";
  if ( permissions & ACLJobs::Post )
    str += "Post ";
  if ( permissions & ACLJobs::Create )
    str += "Create ";
  if ( permissions & ACLJobs::Delete )
    str += "Delete ";
  if ( permissions & ACLJobs::Administer )
    str += "Administer ";
  if ( !str.isEmpty() )
    str.truncate( str.length() - 1 );
  return str;
}
#endif

KIO::SimpleJob* ACLJobs::setACL( KIO::Slave* slave, const KURL& url, const QString& user, unsigned int permissions )
{
  QString perm = QString::fromLatin1( permissionsToIMAPRights( permissions ) );

  QByteArray packedArgs;
  QDataStream stream( packedArgs, IO_WriteOnly );
  stream << (int)'A' << (int)'S' << url << user << perm;

  KIO::SimpleJob* job = KIO::special( url, packedArgs, false );
  KIO::Scheduler::assignJobToSlave( slave, job );
  return job;
}

ACLJobs::DeleteACLJob* ACLJobs::deleteACL( KIO::Slave* slave, const KURL& url, const QString& user )
{
  QByteArray packedArgs;
  QDataStream stream( packedArgs, IO_WriteOnly );
  stream << (int)'A' << (int)'D' << url << user;

  ACLJobs::DeleteACLJob* job = new ACLJobs::DeleteACLJob( url, user, packedArgs, false );
  KIO::Scheduler::assignJobToSlave( slave, job );
  return job;
}

ACLJobs::GetACLJob* ACLJobs::getACL( KIO::Slave* slave, const KURL& url )
{
  QByteArray packedArgs;
  QDataStream stream( packedArgs, IO_WriteOnly );
  stream << (int)'A' << (int)'G' << url;

  ACLJobs::GetACLJob* job = new ACLJobs::GetACLJob( url, packedArgs, false );
  KIO::Scheduler::assignJobToSlave( slave, job );
  return job;
}

ACLJobs::GetUserRightsJob* ACLJobs::getUserRights( KIO::Slave* slave, const KURL& url )
{
  QByteArray packedArgs;
  QDataStream stream( packedArgs, IO_WriteOnly );
  stream << (int)'A' << (int)'M' << url;

  ACLJobs::GetUserRightsJob* job = new ACLJobs::GetUserRightsJob( url, packedArgs, false );
  KIO::Scheduler::assignJobToSlave( slave, job );
  return job;
}

ACLJobs::GetACLJob::GetACLJob( const KURL& url, const QByteArray &packedArgs,
                                 bool showProgressInfo )
  : KIO::SimpleJob( url, KIO::CMD_SPECIAL, packedArgs, showProgressInfo )
{
  connect( this, SIGNAL(infoMessage(KIO::Job*,const QString&)),
           SLOT(slotInfoMessage(KIO::Job*,const QString&)) );
}

void ACLJobs::GetACLJob::slotInfoMessage( KIO::Job*, const QString& str )
{
  // Parse the result
  QStringList lst = QStringList::split( " ", str, true );
  while ( lst.count() >= 2 ) // we take items 2 by 2
  {
    QString user = lst.front(); lst.pop_front();
    QString imapRights = lst.front(); lst.pop_front();
    unsigned int perm = IMAPRightsToPermission( imapRights, url(), user );
    m_entries.append( ACLListEntry( user, imapRights, perm ) );
  }
}

ACLJobs::GetUserRightsJob::GetUserRightsJob( const KURL& url, const QByteArray &packedArgs,
                                               bool showProgressInfo )
  : KIO::SimpleJob( url, KIO::CMD_SPECIAL, packedArgs, showProgressInfo )
{
  connect( this, SIGNAL(infoMessage(KIO::Job*,const QString&)),
           SLOT(slotInfoMessage(KIO::Job*,const QString&)) );
}

void ACLJobs::GetUserRightsJob::slotInfoMessage( KIO::Job*, const QString& str )
{
  // Parse the result
  m_permissions = IMAPRightsToPermission( str, url(), QString::null );
}

ACLJobs::DeleteACLJob::DeleteACLJob( const KURL& url, const QString& userId,
                                     const QByteArray &packedArgs,
                                     bool showProgressInfo )
  : KIO::SimpleJob( url, KIO::CMD_SPECIAL, packedArgs, showProgressInfo ),
    mUserId( userId )
{
}

////

ACLJobs::MultiSetACLJob::MultiSetACLJob( KIO::Slave* slave, const KURL& url, const ACLList& acl, bool showProgressInfo )
  : KIO::Job( showProgressInfo ),
    mSlave( slave ),
    mUrl( url ), mACLList( acl ), mACLListIterator( mACLList.begin() )
{
  QTimer::singleShot(0, this, SLOT(slotStart()));
}

void ACLJobs::MultiSetACLJob::slotStart()
{
  // Skip over unchanged entries
  while ( mACLListIterator != mACLList.end() && !(*mACLListIterator).changed )
    ++mACLListIterator;

  if ( mACLListIterator != mACLList.end() )
  {
    const ACLListEntry& entry = *mACLListIterator;
    KIO::Job* job = 0;
    if ( entry.permissions > -1 )
      job = setACL( mSlave, mUrl, entry.userId, entry.permissions );
    else
      job = deleteACL( mSlave, mUrl, entry.userId );

    addSubjob( job );
  } else { // done!
    emitResult();
  }
}

void ACLJobs::MultiSetACLJob::slotResult( KIO::Job *job )
{
  if ( job->error() ) {
    KIO::Job::slotResult( job ); // will set the error and emit result(this)
    return;
  }
  subjobs.remove(job);
  const ACLListEntry& entry = *mACLListIterator;
  emit aclChanged( entry.userId, entry.permissions );

  // Move on to next one
  ++mACLListIterator;
  slotStart();
}

ACLJobs::MultiSetACLJob* ACLJobs::multiSetACL( KIO::Slave* slave, const KURL& url, const ACLList& acl )
{
  return new MultiSetACLJob( slave, url, acl, false /*showProgressInfo*/ );
}

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


<commit_before>/***************************************************************************
                          main.cpp  -  description
                             -------------------
    begin                : Wed Aug  2 11:23:04 CEST 2000
    copyright            : (C) 2000 by Hans Dijkema
    email                : kmailcvt@hum.org
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/

#include <kcmdlineargs.h>
#include <kaboutdata.h>
#include <dcopclient.h>

#include "kmailcvt.h"

static const KCmdLineOptions options[] =
{
  KCmdLineLastOption
};

int main(int argc, char *argv[])
{
  KLocale::setMainCatalogue("kmailcvt");

  KAboutData aboutData( "kmailcvt", I18N_NOOP("KMailCVT"),
    "3", I18N_NOOP("KMail Import Filters"), KAboutData::License_GPL_V2,
    I18N_NOOP("(c) 2000-2003, The KMailCVT developers"));
  aboutData.addAuthor("Hans Dijkema",I18N_NOOP("Original author"), "kmailcvt@hum.org");
  aboutData.addAuthor("Laurence Anderson", I18N_NOOP("New GUI & cleanups"), "l.d.anderson@warwick.ac.uk");
  aboutData.addCredit("Daniel Molkentin", I18N_NOOP("New GUI & cleanups"), "molkentin@kde.org");

  KCmdLineArgs::init( argc, argv, &aboutData );
  KCmdLineArgs::addCmdLineOptions( options ); // Add our own options.

  KApplication a;
  KMailCVT *kmailcvt = new KMailCVT();
  a.setMainWidget(kmailcvt);
  kmailcvt->show();

  DCOPClient *client=a.dcopClient();
  if (!client->attach()) {
    return 1;
  }

  return a.exec();
}
<commit_msg>- added me to authors<commit_after>/***************************************************************************
                          main.cpp  -  description
                             -------------------
    begin                : Wed Aug  2 11:23:04 CEST 2000
    copyright            : (C) 2000 by Hans Dijkema
    email                : kmailcvt@hum.org
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/

#include <kcmdlineargs.h>
#include <kaboutdata.h>
#include <dcopclient.h>

#include "kmailcvt.h"

static const KCmdLineOptions options[] =
{
  KCmdLineLastOption
};

int main(int argc, char *argv[])
{
  KLocale::setMainCatalogue("kmailcvt");

  KAboutData aboutData( "kmailcvt", I18N_NOOP("KMailCVT"),
    "3", I18N_NOOP("KMail Import Filters"), KAboutData::License_GPL_V2,
    I18N_NOOP("(c) 2000-2003, The KMailCVT developers"));
  aboutData.addAuthor("Hans Dijkema",I18N_NOOP("Original author"), "kmailcvt@hum.org");
  aboutData.addAuthor("Laurence Anderson", I18N_NOOP("New GUI & cleanups"), "l.d.anderson@warwick.ac.uk");
  aboutData.addAuthor("Danny Kukawka", I18N_NOOP("New GUI & cleanups"), "danny.kukawka@web.de");
  aboutData.addCredit("Daniel Molkentin", I18N_NOOP("New GUI & cleanups"), "molkentin@kde.org");

  KCmdLineArgs::init( argc, argv, &aboutData );
  KCmdLineArgs::addCmdLineOptions( options ); // Add our own options.

  KApplication a;
  KMailCVT *kmailcvt = new KMailCVT();
  a.setMainWidget(kmailcvt);
  kmailcvt->show();

  DCOPClient *client=a.dcopClient();
  if (!client->attach()) {
    return 1;
  }

  return a.exec();
}
<|endoftext|>


<commit_before>#include <Common.hpp>
#include <Engine.hpp>
#include <Dispatch.hpp>
#include <MessageProcessor.hpp>

#include <functional>
#include <iostream>
#include <sstream>
#include <fstream>

#include <boost/regex.hpp>
#include <boost/thread/thread.hpp>

namespace K3 {

std::string localhost = "127.0.0.1";
Address peer1;
Address peer2;
Address rendezvous;

int nodeCounter(0);
int numSent(0);
string message;

using std::cout;
using std::endl;
using std::bind;
using std::string;
using std::shared_ptr;

// "Trigger" Declarations
void join(shared_ptr<Engine> engine, string message_contents) {
  Message m = Message(rendezvous, "register", message);
  engine->send(m);
}

void register2(shared_ptr<Engine> engine, string message_contents) {
  int n = 1;
  nodeCounter += n;
  std::cout << "Length (mB)" << message_contents.length() / (1024*1024.0) << ". Number:" << nodeCounter<< std::endl;
}

// MP setup
TriggerDispatch buildTable(shared_ptr<Engine> engine) {
  TriggerDispatch table = TriggerDispatch();
  table["join"] = bind(&K3::join, engine, std::placeholders::_1);
  table["register"] = bind(&K3::register2, engine, std::placeholders::_1);
  return table;
}

shared_ptr<MessageProcessor> buildMP(shared_ptr<Engine> engine) {
  auto table = buildTable(engine);
  shared_ptr<MessageProcessor> mp = make_shared<DispatchMessageProcessor>(DispatchMessageProcessor(table));
  return mp;
}

// Engine setup
shared_ptr<Engine> buildEngine(bool simulation, SystemEnvironment s_env) {
  // Configure engine components
  shared_ptr<InternalCodec> i_cdec = make_shared<LengthHeaderInternalCodec>(LengthHeaderInternalCodec());

  // Construct an engine
  Engine engine = Engine(simulation, s_env, i_cdec);
  return make_shared<Engine>(engine);
}

}

void runReceiver(int num_messages) {
  K3::peer1 = K3::make_address("192.168.0.11", 3000);
  K3::nodeCounter = 0;
  using boost::thread;
  using boost::thread_group;
  using std::shared_ptr;
  // Create engines
  auto engine1 = K3::buildEngine(false, K3::defaultEnvironment(K3::peer1));
  // Create MPs
  auto mp1 = K3::buildMP(engine1);
  // Fork a thread for each engine
  auto service_threads = std::shared_ptr<thread_group>(new thread_group());
  shared_ptr<thread> t1 = engine1->forkEngine(mp1);

  service_threads->add_thread(t1.get());

  int desired = num_messages;
  while ((K3::nodeCounter < desired)) {
   continue;
  }

  engine1->forceTerminateEngine();

  service_threads->join_all();
  service_threads->remove_thread(t1.get());
}

void runSender(int num_messages, int message_len, string rendez) {
  K3::message = "";
  for (int i=0; i< message_len; i++) {
    K3::message += "a";
  }
  using boost::thread;
  using boost::thread_group;
  using std::shared_ptr;
  // Create peers
  K3::peer1 = K3::make_address(rendez, 3000);
  K3::peer2 = K3::make_address(K3::localhost, 3000);
  K3::rendezvous = K3::peer1;
  // Create engines
  auto engine2 = K3::buildEngine(false, K3::defaultEnvironment(K3::peer2));
  // Create MPs
  auto mp2 = K3::buildMP(engine2);
  // Create initial messages (source)
  K3::Message m2 = K3::Message(K3::peer2, "join", "()");
  for (int i = 0; i < num_messages; i++) {
    engine2->send(m2);
  }
  engine2->runEngine(mp2);

}

int main(int argc, char** argv) {

 if (argc < 3) {
  std::cout << "usage: " << argv[0] << " num_messages" << " sender|receiver (IP of receiver)" << std::endl;
  return -1;
 }

 int num_messages = std::atoi(argv[1]);
 std::string mode = argv[2];
 if (mode == "receiver") {
   runReceiver(num_messages);
 }
 else {
   if (argc < 4) {
     std::cout << "must provide ip of reciever!" << std::endl;
     return -1;
   }
   runSender(num_messages, 10, argv[3]);
 }

 return 0;
}
<commit_msg>fixed missing std:: prefix<commit_after>#include <Common.hpp>
#include <Engine.hpp>
#include <Dispatch.hpp>
#include <MessageProcessor.hpp>

#include <functional>
#include <iostream>
#include <sstream>
#include <fstream>

#include <boost/regex.hpp>
#include <boost/thread/thread.hpp>

namespace K3 {

std::string localhost = "127.0.0.1";
Address peer1;
Address peer2;
Address rendezvous;

int nodeCounter(0);
int numSent(0);
string message;

using std::cout;
using std::endl;
using std::bind;
using std::string;
using std::shared_ptr;

// "Trigger" Declarations
void join(shared_ptr<Engine> engine, string message_contents) {
  Message m = Message(rendezvous, "register", message);
  engine->send(m);
}

void register2(shared_ptr<Engine> engine, string message_contents) {
  int n = 1;
  nodeCounter += n;
  std::cout << "Length (mB)" << message_contents.length() / (1024*1024.0) << ". Number:" << nodeCounter<< std::endl;
}

// MP setup
TriggerDispatch buildTable(shared_ptr<Engine> engine) {
  TriggerDispatch table = TriggerDispatch();
  table["join"] = bind(&K3::join, engine, std::placeholders::_1);
  table["register"] = bind(&K3::register2, engine, std::placeholders::_1);
  return table;
}

shared_ptr<MessageProcessor> buildMP(shared_ptr<Engine> engine) {
  auto table = buildTable(engine);
  shared_ptr<MessageProcessor> mp = make_shared<DispatchMessageProcessor>(DispatchMessageProcessor(table));
  return mp;
}

// Engine setup
shared_ptr<Engine> buildEngine(bool simulation, SystemEnvironment s_env) {
  // Configure engine components
  shared_ptr<InternalCodec> i_cdec = make_shared<LengthHeaderInternalCodec>(LengthHeaderInternalCodec());

  // Construct an engine
  Engine engine = Engine(simulation, s_env, i_cdec);
  return make_shared<Engine>(engine);
}

}

void runReceiver(int num_messages) {
  K3::peer1 = K3::make_address("192.168.0.11", 3000);
  K3::nodeCounter = 0;
  using boost::thread;
  using boost::thread_group;
  using std::shared_ptr;
  // Create engines
  auto engine1 = K3::buildEngine(false, K3::defaultEnvironment(K3::peer1));
  // Create MPs
  auto mp1 = K3::buildMP(engine1);
  // Fork a thread for each engine
  auto service_threads = std::shared_ptr<thread_group>(new thread_group());
  shared_ptr<thread> t1 = engine1->forkEngine(mp1);

  service_threads->add_thread(t1.get());

  int desired = num_messages;
  while ((K3::nodeCounter < desired)) {
   continue;
  }

  engine1->forceTerminateEngine();

  service_threads->join_all();
  service_threads->remove_thread(t1.get());
}

void runSender(int num_messages, int message_len, std::string rendez) {
  K3::message = "";
  for (int i=0; i< message_len; i++) {
    K3::message += "a";
  }
  using boost::thread;
  using boost::thread_group;
  using std::shared_ptr;
  // Create peers
  K3::peer1 = K3::make_address(rendez, 3000);
  K3::peer2 = K3::make_address(K3::localhost, 3000);
  K3::rendezvous = K3::peer1;
  // Create engines
  auto engine2 = K3::buildEngine(false, K3::defaultEnvironment(K3::peer2));
  // Create MPs
  auto mp2 = K3::buildMP(engine2);
  // Create initial messages (source)
  K3::Message m2 = K3::Message(K3::peer2, "join", "()");
  for (int i = 0; i < num_messages; i++) {
    engine2->send(m2);
  }
  engine2->runEngine(mp2);

}

int main(int argc, char** argv) {

 if (argc < 3) {
  std::cout << "usage: " << argv[0] << " num_messages" << " sender|receiver (IP of receiver)" << std::endl;
  return -1;
 }

 int num_messages = std::atoi(argv[1]);
 std::string mode = argv[2];
 if (mode == "receiver") {
   runReceiver(num_messages);
 }
 else {
   if (argc < 4) {
     std::cout << "must provide ip of reciever!" << std::endl;
     return -1;
   }
   runSender(num_messages, 10, argv[3]);
 }

 return 0;
}
<|endoftext|>


<commit_before>#include <algorithm>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>

#include <popt.h>

#include "defs.hh"
#include "StringSet.hh"
#include "FactorEncoder.hh"
#include "GreedyUnigrams.hh"

using namespace std;

void assert_single_chars(map<string, flt_type> &vocab,
                         const map<string, flt_type> &chars,
                         flt_type val)
{
    for (auto it = chars.cbegin(); it != chars.cend(); ++it)
        if (vocab.find(it->first) == vocab.end())
            vocab[it->first] = val;
}

int main(int argc, char* argv[]) {

    float cutoff_value = 0.0;
    int min_removals_per_iter = 0;
    float threshold = -25.0;
    float threshold_decrease = 25.0;
    int target_vocab_size = 50000;
    bool enable_forward_backward = false;
    flt_type one_char_min_lp = -25.0;
    string vocab_fname;
    string wordlist_fname;

    // Popt documentation:
    // http://linux.die.net/man/3/popt
    // http://privatemisc.blogspot.fi/2012/12/popt-basic-example.html
    poptContext pc;
    struct poptOption po[] = {
        {"cutoff", 'u', POPT_ARG_FLOAT, &cutoff_value, 11001, NULL, "Cutoff value for each iteration"},
        {"min_removals", 'i', POPT_ARG_INT, &min_removals_per_iter, 11004, NULL, "Minimum number of removals per iteration (stopping criterion)"},
        {"threshold", 't', POPT_ARG_FLOAT, &threshold, 11005, NULL, "Likelihood threshold for removals"},
        {"threshold_decrease", 'd', POPT_ARG_FLOAT, &threshold_decrease, 11006, NULL, "Threshold decrease between iterations"},
        {"vocab_size", 'g', POPT_ARG_INT, &target_vocab_size, 11007, NULL, "Target vocabulary size (stopping criterion)"},
        {"forward_backward", 'f', POPT_ARG_NONE, &enable_forward_backward, 11007, "Use Forward-backward segmentation instead of Viterbi", NULL},
        POPT_AUTOHELP
        {NULL}
    };

    pc = poptGetContext(NULL, argc, (const char **)argv, po, 0);
    poptSetOtherOptionHelp(pc, "[INITIAL VOCABULARY] [WORDLIST]");

    int val;
    while ((val = poptGetNextOpt(pc)) >= 0)
        continue;

    // poptGetNextOpt returns -1 when the final argument has been parsed
    // otherwise an error occured
    if (val != -1) {
        switch (val) {
        case POPT_ERROR_NOARG:
            cerr << "Argument missing for an option" << endl;
            exit(1);
        case POPT_ERROR_BADOPT:
            cerr << "Option's argument could not be parsed" << endl;
            exit(1);
        case POPT_ERROR_BADNUMBER:
        case POPT_ERROR_OVERFLOW:
            cerr << "Option could not be converted to number" << endl;
            exit(1);
        default:
            cerr << "Unknown error in option processing" << endl;
            exit(1);
        }
    }

    // Handle ARG part of command line
    if (poptPeekArg(pc) != NULL)
        vocab_fname.assign((char*)poptGetArg(pc));
    else {
        cerr << "Initial vocabulary file not set" << endl;
        exit(1);
    }

    if (poptPeekArg(pc) != NULL)
        wordlist_fname.assign((char*)poptGetArg(pc));
    else {
        cerr << "Wordlist file not set" << endl;
        exit(1);
    }

    cerr << "parameters, initial vocabulary: " << vocab_fname << endl;
    cerr << "parameters, wordlist: " << wordlist_fname << endl;
    cerr << "parameters, cutoff: " << setprecision(15) << cutoff_value << endl;
    cerr << "parameters, threshold: " << threshold << endl;
    cerr << "parameters, threshold decrease per iteration: " << threshold_decrease << endl;
    cerr << "parameters, min removals per iteration: " << min_removals_per_iter << endl;
    cerr << "parameters, target vocab size: " << target_vocab_size << endl;
    cerr << "parameters, use forward-backward: " << enable_forward_backward << endl;

    int maxlen, word_maxlen;
    map<string, flt_type> all_chars;
    map<string, flt_type> vocab;
    map<string, flt_type> freqs;
    map<string, flt_type> words;

    cerr << "Reading vocabulary " << vocab_fname << endl;
    int retval = read_vocab(vocab_fname, vocab, maxlen);
    if (retval < 0) {
        cerr << "something went wrong reading vocabulary" << endl;
        exit(0);
    }
    cerr << "\t" << "size: " << vocab.size() << endl;
    cerr << "\t" << "maximum string length: " << maxlen << endl;
    for (auto it = vocab.cbegin(); it != vocab.end(); ++it)
        if (it->first.length() == 1)
            all_chars[it->first] = 0.0;

    cerr << "Reading word list " << wordlist_fname << endl;
    retval = read_vocab(wordlist_fname, words, word_maxlen);
    if (retval < 0) {
        cerr << "something went wrong reading word list" << endl;
        exit(0);
    }
    cerr << "\t" << "wordlist size: " << words.size() << endl;
    cerr << "\t" << "maximum word length: " << word_maxlen << endl;

    GreedyUnigrams gg;
    if (enable_forward_backward)
        gg.set_segmentation_method(forward_backward);
    else
        gg.set_segmentation_method(viterbi);

    cerr << "Initial cutoff" << endl;
    gg.resegment_words(words, vocab, freqs);
    flt_type densum = gg.get_sum(freqs);
    flt_type cost = gg.get_cost(freqs, densum);
    cerr << "cost: " << cost << endl;

    gg.cutoff(freqs, (flt_type)cutoff_value);
    cerr << "\tcutoff: " << cutoff_value << "\t" << "vocabulary size: " << freqs.size() << endl;
    vocab = freqs;
    densum = gg.get_sum(vocab);
    gg.freqs_to_logprobs(vocab, densum);
    assert_single_chars(vocab, all_chars, one_char_min_lp);

    cerr << "Removing subwords one by one" << endl;
    int itern = 1;
    while (true) {

        cerr << "iteration " << itern << endl;

        vector<pair<string, flt_type> > sorted_vocab;
        sort_vocab(freqs, sorted_vocab, false);

        // Perform removals one by one if likelihood change above threshold
        flt_type curr_densum = gg.get_sum(freqs);
        flt_type curr_cost = gg.get_cost(freqs, curr_densum);
        map<string, map<string, flt_type> > backpointers;
        gg.get_backpointers(words, vocab, backpointers);

        cerr << "starting cost before removing subwords one by one: " << curr_cost << endl;

        unsigned int n_removals = 0;
        unsigned int non_removals_in_row = 0;
        for (unsigned int i=0; i<sorted_vocab.size(); i++) {

            if (sorted_vocab[i].first.length() == 1) continue;
            if (vocab.find(sorted_vocab[i].first) == vocab.end()) continue;
            if (backpointers.find(sorted_vocab[i].first) == backpointers.end()) {
                vocab.erase(sorted_vocab[i].first);
                continue;
            }

            cout << "trying to remove: " << sorted_vocab[i].first << endl;

            map<string, flt_type> freq_diffs;
            map<string, map<string, flt_type> > backpointers_to_remove;
            map<string, map<string, flt_type> > backpointers_to_add;
            StringSet<flt_type> stringset_vocab(vocab);
            gg.hypo_removal(stringset_vocab, sorted_vocab[i].first, backpointers,
                            backpointers_to_remove, backpointers_to_add, freq_diffs);

            flt_type hypo_densum = gg.get_sum(freqs, freq_diffs);
            flt_type hypo_cost = gg.get_cost(freqs, freq_diffs, hypo_densum);

            cout << sorted_vocab[i].first << "\t" << "change in likelihood: " << hypo_cost-curr_cost;
            if (hypo_cost-curr_cost < threshold) {
                cout << " was below threshold " << threshold << endl;
                non_removals_in_row++;
                if (non_removals_in_row > 1000) break;
                continue;
            }
            non_removals_in_row=0;
            cout << " removed, was above threshold " << threshold << endl;

            gg.apply_freq_diffs(freqs, freq_diffs);
            freqs.erase(sorted_vocab[i].first);
            gg.apply_backpointer_changes(backpointers, backpointers_to_remove, backpointers_to_add);
            backpointers.erase(sorted_vocab[i].first);

            curr_densum = hypo_densum;
            curr_cost = hypo_cost;
            vocab = freqs;
            gg.freqs_to_logprobs(vocab, hypo_densum);
            assert_single_chars(vocab, all_chars, one_char_min_lp);

            n_removals++;

            if (vocab.size() % 5000 == 0) {
                ostringstream vocabfname;
                vocabfname << "iter" << itern << "_" << vocab.size() << ".vocab";
                write_vocab(vocabfname.str(), vocab);
            }

            if (vocab.size() <= target_vocab_size) break;
        }

        int n_cutoff = gg.cutoff(freqs, cutoff_value);
        flt_type co_densum = gg.get_sum(freqs);
        vocab = freqs;
        gg.freqs_to_logprobs(vocab, co_densum);
        assert_single_chars(vocab, all_chars, one_char_min_lp);
        gg.resegment_words(words, vocab, freqs);
        curr_densum = gg.get_sum(freqs);
        curr_cost = gg.get_cost(freqs, densum);

        cerr << "subwords removed in this iteration: " << n_removals << endl;
        cerr << "subwords removed with cutoff this iteration: " << n_cutoff << endl;
        cerr << "current vocabulary size: " << vocab.size() << endl;
        cerr << "likelihood after the removals: " << curr_cost << endl;

        ostringstream vocabfname;
        vocabfname << "iter" << itern << ".vocab";
        write_vocab(vocabfname.str(), vocab);

        itern++;
        threshold -= threshold_decrease;

        if (n_removals < min_removals_per_iter) {
            cerr << "stopping by min_removals_per_iter." << endl;
            break;
        }

        if (vocab.size() <= target_vocab_size) {
            cerr << "stopping by min_vocab_size." << endl;
            break;
        }
    }

    exit(1);
}

<commit_msg>Keep scores internally in log10, output and thresholds in log2.<commit_after>#include <algorithm>
#include <cmath>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>

#include <popt.h>

#include "defs.hh"
#include "StringSet.hh"
#include "FactorEncoder.hh"
#include "GreedyUnigrams.hh"

using namespace std;

void assert_single_chars(map<string, flt_type> &vocab,
                         const map<string, flt_type> &chars,
                         flt_type val)
{
    for (auto it = chars.cbegin(); it != chars.cend(); ++it)
        if (vocab.find(it->first) == vocab.end())
            vocab[it->first] = val;
}

int main(int argc, char* argv[]) {

    float cutoff_value = 0.0;
    int min_removals_per_iter = 0;
    float threshold = -25.0;
    float threshold_decrease = 25.0;
    int target_vocab_size = 50000;
    bool enable_forward_backward = false;
    flt_type one_char_min_lp = -25.0 / log2(10);
    string vocab_fname;
    string wordlist_fname;

    // Popt documentation:
    // http://linux.die.net/man/3/popt
    // http://privatemisc.blogspot.fi/2012/12/popt-basic-example.html
    poptContext pc;
    struct poptOption po[] = {
        {"cutoff", 'u', POPT_ARG_FLOAT, &cutoff_value, 11001, NULL, "Cutoff value for each iteration"},
        {"min_removals", 'i', POPT_ARG_INT, &min_removals_per_iter, 11004, NULL, "Minimum number of removals per iteration (stopping criterion)"},
        {"threshold", 't', POPT_ARG_FLOAT, &threshold, 11005, NULL, "Likelihood threshold for removals"},
        {"threshold_decrease", 'd', POPT_ARG_FLOAT, &threshold_decrease, 11006, NULL, "Threshold decrease between iterations"},
        {"vocab_size", 'g', POPT_ARG_INT, &target_vocab_size, 11007, NULL, "Target vocabulary size (stopping criterion)"},
        {"forward_backward", 'f', POPT_ARG_NONE, &enable_forward_backward, 11007, "Use Forward-backward segmentation instead of Viterbi", NULL},
        POPT_AUTOHELP
        {NULL}
    };

    pc = poptGetContext(NULL, argc, (const char **)argv, po, 0);
    poptSetOtherOptionHelp(pc, "[INITIAL VOCABULARY] [WORDLIST]");

    int val;
    while ((val = poptGetNextOpt(pc)) >= 0)
        continue;

    // poptGetNextOpt returns -1 when the final argument has been parsed
    // otherwise an error occured
    if (val != -1) {
        switch (val) {
        case POPT_ERROR_NOARG:
            cerr << "Argument missing for an option" << endl;
            exit(1);
        case POPT_ERROR_BADOPT:
            cerr << "Option's argument could not be parsed" << endl;
            exit(1);
        case POPT_ERROR_BADNUMBER:
        case POPT_ERROR_OVERFLOW:
            cerr << "Option could not be converted to number" << endl;
            exit(1);
        default:
            cerr << "Unknown error in option processing" << endl;
            exit(1);
        }
    }

    // Handle ARG part of command line
    if (poptPeekArg(pc) != NULL)
        vocab_fname.assign((char*)poptGetArg(pc));
    else {
        cerr << "Initial vocabulary file not set" << endl;
        exit(1);
    }

    if (poptPeekArg(pc) != NULL)
        wordlist_fname.assign((char*)poptGetArg(pc));
    else {
        cerr << "Wordlist file not set" << endl;
        exit(1);
    }

    cerr << "parameters, initial vocabulary: " << vocab_fname << endl;
    cerr << "parameters, wordlist: " << wordlist_fname << endl;
    cerr << "parameters, cutoff: " << setprecision(15) << cutoff_value << endl;
    cerr << "parameters, threshold: " << threshold << endl;
    cerr << "parameters, threshold decrease per iteration: " << threshold_decrease << endl;
    cerr << "parameters, min removals per iteration: " << min_removals_per_iter << endl;
    cerr << "parameters, target vocab size: " << target_vocab_size << endl;
    cerr << "parameters, use forward-backward: " << enable_forward_backward << endl;

    int maxlen, word_maxlen;
    map<string, flt_type> all_chars;
    map<string, flt_type> vocab;
    map<string, flt_type> freqs;
    map<string, flt_type> words;

    cerr << "Reading vocabulary " << vocab_fname << endl;
    int retval = read_vocab(vocab_fname, vocab, maxlen);
    if (retval < 0) {
        cerr << "something went wrong reading vocabulary" << endl;
        exit(0);
    }
    cerr << "\t" << "size: " << vocab.size() << endl;
    cerr << "\t" << "maximum string length: " << maxlen << endl;
    for (auto it = vocab.cbegin(); it != vocab.end(); ++it)
        if (it->first.length() == 1)
            all_chars[it->first] = 0.0;

    cerr << "Reading word list " << wordlist_fname << endl;
    retval = read_vocab(wordlist_fname, words, word_maxlen);
    if (retval < 0) {
        cerr << "something went wrong reading word list" << endl;
        exit(0);
    }
    cerr << "\t" << "wordlist size: " << words.size() << endl;
    cerr << "\t" << "maximum word length: " << word_maxlen << endl;

    GreedyUnigrams gg;
    if (enable_forward_backward)
        gg.set_segmentation_method(forward_backward);
    else
        gg.set_segmentation_method(viterbi);

    gg.resegment_words(words, vocab, freqs);
    flt_type densum = gg.get_sum(freqs);
    flt_type cost = gg.get_cost(freqs, densum);
    cerr << "cost: " << cost * log2(10.0) << endl;

    cerr << "Initial cutoff" << endl;
    gg.cutoff(freqs, (flt_type)cutoff_value);
    cerr << "\tcutoff: " << cutoff_value << "\t" << "vocabulary size: " << freqs.size() << endl;
    densum = gg.get_sum(freqs);
    cost = gg.get_cost(freqs, densum);
    cerr << "cost: " << cost * log2(10.0) << endl;
    vocab = freqs;
    gg.freqs_to_logprobs(vocab, densum);
    assert_single_chars(vocab, all_chars, one_char_min_lp);

    cerr << "Removing subwords one by one" << endl;
    int itern = 1;
    while (true) {

        cerr << "iteration " << itern << endl;

        vector<pair<string, flt_type> > sorted_vocab;
        sort_vocab(freqs, sorted_vocab, false);

        // Perform removals one by one if likelihood change above threshold
        flt_type curr_densum = gg.get_sum(freqs);
        flt_type curr_cost = gg.get_cost(freqs, curr_densum);
        map<string, map<string, flt_type> > backpointers;
        gg.get_backpointers(words, vocab, backpointers);

        cerr << "starting cost before removing subwords one by one: " << curr_cost * log2(10.0) << endl;

        unsigned int n_removals = 0;
        unsigned int non_removals_in_row = 0;
        for (unsigned int i=0; i<sorted_vocab.size(); i++) {

            if (sorted_vocab[i].first.length() == 1) continue;
            if (vocab.find(sorted_vocab[i].first) == vocab.end()) continue;
            if (backpointers.find(sorted_vocab[i].first) == backpointers.end()) {
                vocab.erase(sorted_vocab[i].first);
                continue;
            }

            cout << "trying to remove: " << sorted_vocab[i].first << endl;

            map<string, flt_type> freq_diffs;
            map<string, map<string, flt_type> > backpointers_to_remove;
            map<string, map<string, flt_type> > backpointers_to_add;
            StringSet<flt_type> stringset_vocab(vocab);
            gg.hypo_removal(stringset_vocab, sorted_vocab[i].first, backpointers,
                            backpointers_to_remove, backpointers_to_add, freq_diffs);

            flt_type hypo_densum = gg.get_sum(freqs, freq_diffs);
            flt_type hypo_cost = gg.get_cost(freqs, freq_diffs, hypo_densum);

            cout << sorted_vocab[i].first << "\t" << "change in likelihood: " << (hypo_cost-curr_cost) * log2(10.0);
            if ((hypo_cost-curr_cost) * log2(10.0) < threshold) {
                cout << " was below threshold " << threshold << endl;
                non_removals_in_row++;
                if (non_removals_in_row > 1000) break;
                continue;
            }
            non_removals_in_row=0;
            cout << " removed, was above threshold " << threshold << endl;

            gg.apply_freq_diffs(freqs, freq_diffs);
            freqs.erase(sorted_vocab[i].first);
            gg.apply_backpointer_changes(backpointers, backpointers_to_remove, backpointers_to_add);
            backpointers.erase(sorted_vocab[i].first);

            curr_densum = hypo_densum;
            curr_cost = hypo_cost;
            vocab = freqs;
            gg.freqs_to_logprobs(vocab, hypo_densum);
            assert_single_chars(vocab, all_chars, one_char_min_lp);

            n_removals++;

            if (vocab.size() % 5000 == 0) {
                ostringstream vocabfname;
                vocabfname << "iter" << itern << "_" << vocab.size() << ".vocab";
                write_vocab(vocabfname.str(), vocab);
            }

            if (vocab.size() <= target_vocab_size) break;
        }

        int n_cutoff = gg.cutoff(freqs, cutoff_value);
        flt_type co_densum = gg.get_sum(freqs);
        vocab = freqs;
        gg.freqs_to_logprobs(vocab, co_densum);
        assert_single_chars(vocab, all_chars, one_char_min_lp);
        gg.resegment_words(words, vocab, freqs);
        curr_densum = gg.get_sum(freqs);
        curr_cost = gg.get_cost(freqs, densum);

        cerr << "subwords removed in this iteration: " << n_removals << endl;
        cerr << "subwords removed with cutoff this iteration: " << n_cutoff << endl;
        cerr << "current vocabulary size: " << vocab.size() << endl;
        cerr << "likelihood after the removals: " << curr_cost * log2(10.0) << endl;

        ostringstream vocabfname;
        vocabfname << "iter" << itern << ".vocab";
        write_vocab(vocabfname.str(), vocab);

        itern++;
        threshold -= threshold_decrease;

        if (n_removals < min_removals_per_iter) {
            cerr << "stopping by min_removals_per_iter." << endl;
            break;
        }

        if (vocab.size() <= target_vocab_size) {
            cerr << "stopping by min_vocab_size." << endl;
            break;
        }
    }

    exit(1);
}

<|endoftext|>


<commit_before>/*
 * game: the game of arithmetic - interactive gui - implementation
 * Copyright(c) 2005 by wave++ "Yuri D'Elia" <wavexx@users.sf.net>
 */

/*
 * Headers
 */

#include "game.hh"
#include "interp.hh"
using std::string;

#include <algorithm>
using std::max;

#include <cstdlib>
using std::sprintf;

#include <GL/freeglut.h>

/*
 * Implementation
 */

void
Game::drawFWString(const char* str, const Point& pos)
{
  glPushMatrix();
  glTranslatef(pos.x, pos.y, 0.);

  for(int c = 0; *str; ++str, ++c)
  {
    // center the character
    glPushMatrix();

    const Bounds& cur = resources.cm->getChar(*str).metrics.bounds;
    glTranslatef(
	(nmHSpace * c) + (nmHSpace - (cur.ur.x - cur.ll.x)) / 2 - cur.ll.x,
	(nmVSpace - (cur.ur.y - cur.ll.y)) / 2 - cur.ll.y, 0.);

    resources.cm->draw(*str);
    glPopMatrix();
  }

  glPopMatrix();
}

void
Game::drawStackElem(const Question& q, const Point& pos)
{
  // get the correct color
  Color color = (q.errs? resources.error:
      interpolate(q.cum / max(1., avgTime),
	  resources.normal, resources.timeout));

  // background
  color.rgba[3] = 0.5;
  glColor4fv(color);
  glBegin(GL_QUADS);
  glVertex2f(pos.x, pos.y);
  glVertex2f(pos.x, pos.y + nmVSpace);
  glVertex2f(pos.x + stackW, pos.y + nmVSpace);
  glVertex2f(pos.x + stackW, pos.y);
  glEnd();

  // contour
  color.rgba[3] = 1.;
  glColor4fv(color);
  glBegin(GL_LINE_STRIP);
  glVertex2f(pos.x, pos.y + nmVSpace);
  glVertex2f(pos.x + stackW, pos.y + nmVSpace);
  glVertex2f(pos.x + stackW, pos.y);
  glEnd();

  // string
  char fmt[32];
  sprintf(fmt, "%%%dd%%c%%%dd", maxLenA, maxLenB);

  char* buf = new char[maxLen + 1];
  sprintf(buf, fmt, q.na, q.kernel->first.o->sym(), q.kernel->first.b);

  glColor4fv(resources.foreground);
  drawFWString(buf, pos);
  delete []buf;
}

void
Game::drawBar()
{
  Color color;

  // background
  glBegin(GL_QUADS);

  color = resources.bar[0];
  color.rgba[3] = 0.5;
  glColor4fv(color);
  glVertex2f(urGeom.x - nmVSpace, urGeom.y);
  glVertex2f(urGeom.x, urGeom.y);

  color = resources.bar[1];
  color.rgba[3] = 0.5;
  glColor4fv(color);
  glVertex2f(urGeom.x, 0);
  glVertex2f(urGeom.x - nmVSpace, 0);

  glEnd();

  if(state == playing)
  {
    float r = interpLinear(frame, now);
    float ry = urGeom.y - r * urGeom.y;

    // hilight
    color = interpolate(r, resources.bar[0], resources.bar[1]);
    glColor4fv(color);
    glBegin(GL_QUADS);
    glVertex2f(urGeom.x - nmVSpace, 0);
    glVertex2f(urGeom.x - nmVSpace, ry);
    glVertex2f(urGeom.x, ry);
    glVertex2f(urGeom.x, 0);
    glEnd();

    // timer
    glPushMatrix();
    const Font& font = resources.cm->getFont();
    glTranslatef(urGeom.x - nmVSpace - font.bounds.ll.y,
	urGeom.y + font.bounds.ll.x, 0);
    glRotatef(-90., 0., 0., 1.);
    char buf[sizeof(int) * 4];
    sprintf(buf, "%d", static_cast<int>(frame.end - now));
    glColor4fv(resources.background);
    resources.cm->draw(buf);
    glPopMatrix();
  }

  // separator
  glColor4fv(resources.foreground);
  glBegin(GL_LINES);
  glVertex2f(urGeom.x - nmVSpace, 0);
  glVertex2f(urGeom.x - nmVSpace, urGeom.y);
  glEnd();
}

void
Game::drawAnswer(const float y)
{
  string buf("=");
  buf += answer;

  glColor4fv(resources.foreground);
  drawFWString(buf.c_str(), Point(stackW, y));
}

void
Game::updateAvg()
{
  // time averages
  AvgTime res;
  for(TimeMap::iterator it = data.times.begin(); it != data.times.end(); ++it)
  {
    // only for the active operators
    if(data.ops.find(it->first) != data.ops.end())
      res.add(it->second.avg());
  }
  avgTime = res.avg();
}

void
Game::updateTimes(const Time shift)
{
  playingTime += shift;
  question.cum += shift;
}

void
Game::resetQuestion()
{
  question.kernel = randFind(data.kernels, NrPred(*this));
  const Kernel& kernel(question.kernel->first);
  question.r = kernel.o->result(kernel.a, kernel.b, question.na);
  question.cum = 0.;
  question.errs = 0;
}

void
Game::initAnim()
{
  // basic animation data
  state = animating;
  frame.begin = now;
  frame.end = now + resources.animTime;
}

void
Game::initGame()
{
  // basic animation data
  state = playing;
  frame.begin = now;
  frame.end = now + avgTime;
}

void
Game::drawState()
{
  // always draw the stack
  QDeque::iterator it = stack.begin();
  for(int i = 0; it != stack.end(); ++i, ++it)
    drawStackElem(*it, Point(0, nmVSpace * i));

  // common variables
  float stackHead = nmVSpace * stack.size();

  if(state == animating)
  {
    // animating parts
    float oldHead = nmVSpace * oldSize;
    float ci = interpCubic(frame, now);

    // dropping element
    if(static_cast<int>(stack.size()) >= oldSize)
      drawStackElem(question, interpolate(ci,
	      Point(0, urGeom.y), Point(0, stackHead)));
    else
      drawStackElem(question, Point(0, stackHead));

    // removing element
    if(lastResult)
      drawStackElem(lastQuestion, Point(-ci * stackW * 2, oldHead));

    // answer symbol
    drawAnswer(interpolate(ci, oldHead, stackHead));
  }
  else
  {
    // current element and answer
    drawStackElem(question, Point(0, stackHead));
    drawAnswer(stackHead);
  }

  // remaining time
  drawBar();
}

void
Game::nextState()
{
  if(state == animating)
    initGame();
  else if(state == playing)
    timeOut();
}

void
Game::contGame()
{
  // check game status
  if(stack.size() == data.stackSize)
    stopGame();
  else
  {
    // continue
    answer.clear();
    initAnim();
  }
}

void
Game::stopGame()
{
  state = gameOver;
}

void
Game::timeOut()
{
  // update stack
  lastResult = false;
  oldSize = stack.size();
  stack.push_back(question);
  resetQuestion();
  contGame();
}

void
Game::submitAnswer()
{
  // check result
  int r = strtol(answer.c_str(), NULL, 10);
  lastResult = (r == question.r);
  oldSize = stack.size();

  if(lastResult)
  {
    // correct, update the stack
    lastQuestion = question;
    data.update(question.kernel, question.errs, question.cum);
    updateAvg();

    if(stack.size())
    {
      // recover and old question
      question = stack.back();
      stack.pop_back();
    }
    else
      resetQuestion();
  }
  else
  {
    // wrong answers
    ++question.errs;
    stack.push_back(question);
    resetQuestion();
  }

  contGame();
}

bool
Game::NrPred::operator()(const Kernel& kernel) const
{
  // exclude disabled operations
  if(ref.data.ops.find(kernel.o) == ref.data.ops.end())
    return false;

  // exclude all kernels already present in the stack
  for(QDeque::const_iterator it = ref.stack.begin();
      it != ref.stack.end(); ++it)
    if(kernel == it->kernel->first)
      return false;

  return true;
}

Game::Game(const Resources& resources, const Time now, GameData& data)
: State(resources, now, data)
{
  // font spacing
  Bounds nmBounds;
  resources.cm->maxOf(nmBounds, "0123456789+-*/=~");
  nmHSpace = nmBounds.ur.x - nmBounds.ll.x;
  nmHSpace += nmHSpace * 0.1;
  nmVSpace = 1.1;

  // sizes
  maxLenA = maxLenB = maxLenR = 0;
  for(KernMap::iterator it = data.kernels.begin();
      it != data.kernels.end(); ++it)
  {
    // only consider active operations
    if(data.ops.find(it->first.o) == data.ops.end())
      continue;

    int na;
    char buf[sizeof(int) * 4];
    int r = it->first.o->result(it->first.a, it->first.b, na);
    maxLenA = max(maxLenA, sprintf(buf, "%d", na));
    maxLenB = max(maxLenB, sprintf(buf, "%d", it->first.b));
    maxLenR = max(maxLenR, sprintf(buf, "%d", r));
  }

  // misc geometry
  maxLen = maxLenA + 1 + maxLenB;
  stackW = nmHSpace * maxLen;

  // initial state
  playingTime = 0.;
  this->now = now;
  lastResult = false;
  oldSize = -1;
  updateAvg();
  resetQuestion();
  initAnim();
}

void
Game::reshape(const int w, const int h)
{
  // calculate enough vertical space to fit the stack exactly
  urGeom.y = nmVSpace * data.stackSize;
  urGeom.x = urGeom.y * static_cast<float>(w) / h * resources.videoRatio;
  gluOrtho2D(0, urGeom.x, 0, urGeom.y);
}

void
Game::display(const Time now)
{
  // update times
  if(state == playing)
    updateTimes(now - this->now);
  this->now = now;

  // update status
  if(now > frame.end)
    nextState();

  // actual drawing
  drawState();
}

void
Game::keyboard(const unsigned char key)
{
  if(state != playing)
    return;

  if(key == 8)
  {
    // backspace
    if(answer.size())
      answer.resize(answer.size() - 1);
  }
  else if(key >= '0' && key <= '9')
  {
    // number
    if(answer.size() < maxLenR)
      answer += key;
  }
  else if(key == 13)
  {
    // empty answers as timeouts
    if(answer.size())
      submitAnswer();
    else
      timeOut();
  }
}
<commit_msg>Implemented "practice" and "veryHard".<commit_after>/*
 * game: the game of arithmetic - interactive gui - implementation
 * Copyright(c) 2005 by wave++ "Yuri D'Elia" <wavexx@users.sf.net>
 */

/*
 * Headers
 */

#include "game.hh"
#include "interp.hh"
using std::string;

#include <algorithm>
using std::max;

#include <cstdlib>
using std::sprintf;

#include <GL/freeglut.h>

/*
 * Implementation
 */

void
Game::drawFWString(const char* str, const Point& pos)
{
  glPushMatrix();
  glTranslatef(pos.x, pos.y, 0.);

  for(int c = 0; *str; ++str, ++c)
  {
    // center the character
    glPushMatrix();

    const Bounds& cur = resources.cm->getChar(*str).metrics.bounds;
    glTranslatef(
	(nmHSpace * c) + (nmHSpace - (cur.ur.x - cur.ll.x)) / 2 - cur.ll.x,
	(nmVSpace - (cur.ur.y - cur.ll.y)) / 2 - cur.ll.y, 0.);

    resources.cm->draw(*str);
    glPopMatrix();
  }

  glPopMatrix();
}

void
Game::drawStackElem(const Question& q, const Point& pos)
{
  // get the correct color
  Color color = (q.errs? resources.error:
      interpolate(q.cum / max(1., avgTime),
	  resources.normal, resources.timeout));

  // background
  color.rgba[3] = 0.5;
  glColor4fv(color);
  glBegin(GL_QUADS);
  glVertex2f(pos.x, pos.y);
  glVertex2f(pos.x, pos.y + nmVSpace);
  glVertex2f(pos.x + stackW, pos.y + nmVSpace);
  glVertex2f(pos.x + stackW, pos.y);
  glEnd();

  // contour
  color.rgba[3] = 1.;
  glColor4fv(color);
  glBegin(GL_LINE_STRIP);
  glVertex2f(pos.x, pos.y + nmVSpace);
  glVertex2f(pos.x + stackW, pos.y + nmVSpace);
  glVertex2f(pos.x + stackW, pos.y);
  glEnd();

  // string
  char fmt[32];
  sprintf(fmt, "%%%dd%%c%%%dd", maxLenA, maxLenB);

  char* buf = new char[maxLen + 1];
  sprintf(buf, fmt, q.na, q.kernel->first.o->sym(), q.kernel->first.b);

  glColor4fv(resources.foreground);
  drawFWString(buf, pos);
  delete []buf;
}

void
Game::drawBar()
{
  Color color;

  // background
  glBegin(GL_QUADS);

  color = resources.bar[0];
  color.rgba[3] = 0.5;
  glColor4fv(color);
  glVertex2f(urGeom.x - nmVSpace, urGeom.y);
  glVertex2f(urGeom.x, urGeom.y);

  color = resources.bar[1];
  color.rgba[3] = 0.5;
  glColor4fv(color);
  glVertex2f(urGeom.x, 0);
  glVertex2f(urGeom.x - nmVSpace, 0);

  glEnd();

  if(state == playing)
  {
    float r = interpLinear(frame, now);
    float ry = urGeom.y - r * urGeom.y;

    // hilight
    color = interpolate(r, resources.bar[0], resources.bar[1]);
    glColor4fv(color);
    glBegin(GL_QUADS);
    glVertex2f(urGeom.x - nmVSpace, 0);
    glVertex2f(urGeom.x - nmVSpace, ry);
    glVertex2f(urGeom.x, ry);
    glVertex2f(urGeom.x, 0);
    glEnd();

    // timer
    glPushMatrix();
    const Font& font = resources.cm->getFont();
    glTranslatef(urGeom.x - nmVSpace - font.bounds.ll.y,
	urGeom.y + font.bounds.ll.x, 0);
    glRotatef(-90., 0., 0., 1.);
    char buf[sizeof(int) * 4];
    sprintf(buf, "%d", static_cast<int>(frame.end - now));
    glColor4fv(resources.background);
    resources.cm->draw(buf);
    glPopMatrix();
  }

  // separator
  glColor4fv(resources.foreground);
  glBegin(GL_LINES);
  glVertex2f(urGeom.x - nmVSpace, 0);
  glVertex2f(urGeom.x - nmVSpace, urGeom.y);
  glEnd();
}

void
Game::drawAnswer(const float y)
{
  string buf("=");
  buf += answer;

  glColor4fv(resources.foreground);
  drawFWString(buf.c_str(), Point(stackW, y));
}

void
Game::updateAvg()
{
  // time averages
  AvgTime res;
  for(TimeMap::iterator it = data.times.begin(); it != data.times.end(); ++it)
  {
    // only for the active operators
    if(data.ops.find(it->first) != data.ops.end())
      res.add(it->second.avg());
  }
  avgTime = res.avg();
}

void
Game::updateTimes(const Time shift)
{
  playingTime += shift;
  question.cum += shift;
}

void
Game::resetQuestion()
{
  question.kernel = randFind(data.kernels, NrPred(*this));
  const Kernel& kernel(question.kernel->first);
  question.r = kernel.o->result(kernel.a, kernel.b, question.na);
  question.cum = 0.;
  question.errs = 0;
}

void
Game::initAnim()
{
  // basic animation data
  state = animating;
  frame.begin = now;
  frame.end = now + resources.animTime;
}

void
Game::initGame()
{
  // basic animation data
  state = playing;
  frame.begin = now;
  frame.end = now + avgTime;
}

void
Game::drawState()
{
  // always draw the stack
  QDeque::iterator it = stack.begin();
  for(int i = 0; it != stack.end(); ++i, ++it)
    drawStackElem(*it, Point(0, nmVSpace * i));

  // common variables
  float stackHead = nmVSpace * stack.size();

  if(state == animating)
  {
    // animating parts
    float oldHead = nmVSpace * oldSize;
    float ci = interpCubic(frame, now);

    // dropping element
    if(static_cast<int>(stack.size()) >= oldSize)
      drawStackElem(question, interpolate(ci,
	      Point(0, urGeom.y), Point(0, stackHead)));
    else
      drawStackElem(question, Point(0, stackHead));

    // removing element
    if(lastResult)
      drawStackElem(lastQuestion, Point(-ci * stackW * 2, oldHead));

    // answer symbol
    drawAnswer(interpolate(ci, oldHead, stackHead));
  }
  else
  {
    // current element and answer
    drawStackElem(question, Point(0, stackHead));
    drawAnswer(stackHead);
  }

  // remaining time
  drawBar();
}

void
Game::nextState()
{
  if(state == animating)
    initGame();
  else if(state == playing && data.mode != GameData::practice)
    timeOut();
}

void
Game::contGame()
{
  // check game status
  if(stack.size() == data.stackSize)
    stopGame();
  else
  {
    // continue
    answer.clear();
    initAnim();
  }
}

void
Game::stopGame()
{
  state = gameOver;
}

void
Game::timeOut()
{
  // update stack
  lastResult = false;
  oldSize = stack.size();
  stack.push_back(question);
  resetQuestion();
  contGame();
}

void
Game::submitAnswer()
{
  // check result
  int r = strtol(answer.c_str(), NULL, 10);
  lastResult = (r == question.r);
  oldSize = stack.size();

  if(lastResult)
  {
    // correct, update the stack
    lastQuestion = question;
    data.update(question.kernel, question.errs, question.cum);
    updateAvg();

    if(stack.size() && data.mode != GameData::veryHard)
    {
      // recover and old question
      question = stack.back();
      stack.pop_back();
    }
    else
      resetQuestion();
  }
  else
  {
    // wrong answers
    ++question.errs;
    stack.push_back(question);
    resetQuestion();
  }

  contGame();
}

bool
Game::NrPred::operator()(const Kernel& kernel) const
{
  // exclude disabled operations
  if(ref.data.ops.find(kernel.o) == ref.data.ops.end())
    return false;

  // exclude all kernels already present in the stack
  for(QDeque::const_iterator it = ref.stack.begin();
      it != ref.stack.end(); ++it)
    if(kernel == it->kernel->first)
      return false;

  return true;
}

Game::Game(const Resources& resources, const Time now, GameData& data)
: State(resources, now, data)
{
  // font spacing
  Bounds nmBounds;
  resources.cm->maxOf(nmBounds, "0123456789+-*/=~");
  nmHSpace = nmBounds.ur.x - nmBounds.ll.x;
  nmHSpace += nmHSpace * 0.1;
  nmVSpace = 1.1;

  // sizes
  maxLenA = maxLenB = maxLenR = 0;
  for(KernMap::iterator it = data.kernels.begin();
      it != data.kernels.end(); ++it)
  {
    // only consider active operations
    if(data.ops.find(it->first.o) == data.ops.end())
      continue;

    int na;
    char buf[sizeof(int) * 4];
    int r = it->first.o->result(it->first.a, it->first.b, na);
    maxLenA = max(maxLenA, sprintf(buf, "%d", na));
    maxLenB = max(maxLenB, sprintf(buf, "%d", it->first.b));
    maxLenR = max(maxLenR, sprintf(buf, "%d", r));
  }

  // misc geometry
  maxLen = maxLenA + 1 + maxLenB;
  stackW = nmHSpace * maxLen;

  // initial state
  playingTime = 0.;
  this->now = now;
  lastResult = false;
  oldSize = -1;
  updateAvg();
  resetQuestion();
  initAnim();
}

void
Game::reshape(const int w, const int h)
{
  // calculate enough vertical space to fit the stack exactly
  urGeom.y = nmVSpace * data.stackSize;
  urGeom.x = urGeom.y * static_cast<float>(w) / h * resources.videoRatio;
  gluOrtho2D(0, urGeom.x, 0, urGeom.y);
}

void
Game::display(const Time now)
{
  // update times
  if(state == playing)
    updateTimes(now - this->now);
  this->now = now;

  // update status
  if(now > frame.end)
    nextState();

  // actual drawing
  drawState();
}

void
Game::keyboard(const unsigned char key)
{
  if(state != playing)
    return;

  if(key == 8)
  {
    // backspace
    if(answer.size())
      answer.resize(answer.size() - 1);
  }
  else if(key >= '0' && key <= '9')
  {
    // number
    if(answer.size() < maxLenR)
      answer += key;
  }
  else if(key == 13)
  {
    // empty answers as timeouts
    if(answer.size())
      submitAnswer();
    else
      timeOut();
  }
}
<|endoftext|>