<commit_before>/****************************************************************************
 *
 *   Copyright (c) 2013, 2014 PX4 Development Team. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 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 commander_helper.cpp
 * Commander helper functions implementations
 *
 * @author Thomas Gubler <thomasgubler@student.ethz.ch>
 * @author Julian Oes <julian@oes.ch>
 * @author Anton Babushkin <anton.babushkin@me.com>
 *
 */

#include <stdio.h>
#include <unistd.h>
#include <stdint.h>
#include <stdbool.h>
#include <fcntl.h>
#include <math.h>
#include <string.h>

#include <uORB/uORB.h>
#include <uORB/topics/vehicle_status.h>
#include <uORB/topics/actuator_controls.h>
#include <uORB/topics/vehicle_control_mode.h>
#include <systemlib/err.h>
#include <systemlib/param/param.h>
#include <drivers/drv_hrt.h>
#include <drivers/drv_tone_alarm.h>
#include <drivers/drv_led.h>
#include <drivers/drv_rgbled.h>

#include "commander_helper.h"

/* oddly, ERROR is not defined for c++ */
#ifdef ERROR
# undef ERROR
#endif
static const int ERROR = -1;

#define BLINK_MSG_TIME	700000	// 3 fast blinks

bool is_multirotor(const struct vehicle_status_s *current_status)
{
	return ((current_status->system_type == vehicle_status_s::VEHICLE_TYPE_QUADROTOR) ||
		(current_status->system_type == vehicle_status_s::VEHICLE_TYPE_HEXAROTOR) ||
		(current_status->system_type == vehicle_status_s::VEHICLE_TYPE_OCTOROTOR) ||
		(current_status->system_type == vehicle_status_s::VEHICLE_TYPE_TRICOPTER));
}

bool is_rotary_wing(const struct vehicle_status_s *current_status)
{
	return is_multirotor(current_status) || (current_status->system_type == vehicle_status_s::VEHICLE_TYPE_HELICOPTER)
	       || (current_status->system_type == vehicle_status_s::VEHICLE_TYPE_COAXIAL);
}

bool is_vtol(const struct vehicle_status_s * current_status) {
	return (current_status->system_type == vehicle_status_s::VEHICLE_TYPE_VTOL_DUOROTOR ||
		current_status->system_type == vehicle_status_s::VEHICLE_TYPE_VTOL_QUADROTOR ||
		current_status->system_type == vehicle_status_s::VEHICLE_TYPE_VTOL_HEXAROTOR ||
		current_status->system_type == vehicle_status_s::VEHICLE_TYPE_VTOL_OCTOROTOR);
}

static int buzzer = -1;
static hrt_abstime blink_msg_end = 0;	// end time for currently blinking LED message, 0 if no blink message
static hrt_abstime tune_end = 0;		// end time of currently played tune, 0 for repeating tunes or silence
static int tune_current = TONE_STOP_TUNE;		// currently playing tune, can be interrupted after tune_end
static unsigned int tune_durations[TONE_NUMBER_OF_TUNES];

static param_t bat_v_empty_h;
static param_t bat_v_full_h;
static param_t bat_n_cells_h;
static param_t bat_capacity_h;
static param_t bat_v_load_drop_h;
static float bat_v_empty = 3.4f;
static float bat_v_full = 4.2f;
static float bat_v_load_drop = 0.06f;
static int bat_n_cells = 3;
static float bat_capacity = -1.0f;
static unsigned int counter = 0;

int battery_init()
{
	bat_v_empty_h = param_find("BAT_V_EMPTY");
	bat_v_full_h = param_find("BAT_V_CHARGED");
	bat_n_cells_h = param_find("BAT_N_CELLS");
	bat_capacity_h = param_find("BAT_CAPACITY");
	bat_v_load_drop_h = param_find("BAT_V_LOAD_DROP");

	return OK;
}

int buzzer_init()
{
	tune_end = 0;
	tune_current = 0;
	memset(tune_durations, 0, sizeof(tune_durations));
	tune_durations[TONE_NOTIFY_POSITIVE_TUNE] = 800000;
	tune_durations[TONE_NOTIFY_NEGATIVE_TUNE] = 900000;
	tune_durations[TONE_NOTIFY_NEUTRAL_TUNE] = 500000;
	tune_durations[TONE_ARMING_WARNING_TUNE] = 3000000;

	buzzer = open(TONEALARM0_DEVICE_PATH, O_WRONLY);

	if (buzzer < 0) {
		warnx("Buzzer: open fail\n");
		return ERROR;
	}

	return OK;
}

void buzzer_deinit()
{
	close(buzzer);
}

void set_tune_override(int tune)
{
	ioctl(buzzer, TONE_SET_ALARM, tune);
}

void set_tune(int tune)
{
	unsigned int new_tune_duration = tune_durations[tune];

	/* don't interrupt currently playing non-repeating tune by repeating */
	if (tune_end == 0 || new_tune_duration != 0 || hrt_absolute_time() > tune_end) {
		/* allow interrupting current non-repeating tune by the same tune */
		if (tune != tune_current || new_tune_duration != 0) {
			ioctl(buzzer, TONE_SET_ALARM, tune);
		}

		tune_current = tune;

		if (new_tune_duration != 0) {
			tune_end = hrt_absolute_time() + new_tune_duration;

		} else {
			tune_end = 0;
		}
	}
}

void tune_home_set(bool use_buzzer)
{
	blink_msg_end = hrt_absolute_time() + BLINK_MSG_TIME;
	rgbled_set_color(RGBLED_COLOR_GREEN);
	rgbled_set_mode(RGBLED_MODE_BLINK_FAST);

	if (use_buzzer) {
		set_tune(TONE_HOME_SET);
	}
}

void tune_mission_ok(bool use_buzzer)
{
	blink_msg_end = hrt_absolute_time() + BLINK_MSG_TIME;
	rgbled_set_color(RGBLED_COLOR_GREEN);
	rgbled_set_mode(RGBLED_MODE_BLINK_FAST);

	if (use_buzzer) {
		set_tune(TONE_NOTIFY_NEUTRAL_TUNE);
	}
}

void tune_mission_fail(bool use_buzzer)
{
	blink_msg_end = hrt_absolute_time() + BLINK_MSG_TIME;
	rgbled_set_color(RGBLED_COLOR_GREEN);
	rgbled_set_mode(RGBLED_MODE_BLINK_FAST);

	if (use_buzzer) {
		set_tune(TONE_NOTIFY_NEGATIVE_TUNE);
	}
}

/**
 * Blink green LED and play positive tune (if use_buzzer == true).
 */
void tune_positive(bool use_buzzer)
{
	blink_msg_end = hrt_absolute_time() + BLINK_MSG_TIME;
	rgbled_set_color(RGBLED_COLOR_GREEN);
	rgbled_set_mode(RGBLED_MODE_BLINK_FAST);

	if (use_buzzer) {
		set_tune(TONE_NOTIFY_POSITIVE_TUNE);
	}
}

/**
 * Blink white LED and play neutral tune (if use_buzzer == true).
 */
void tune_neutral(bool use_buzzer)
{
	blink_msg_end = hrt_absolute_time() + BLINK_MSG_TIME;
	rgbled_set_color(RGBLED_COLOR_WHITE);
	rgbled_set_mode(RGBLED_MODE_BLINK_FAST);

	if (use_buzzer) {
		set_tune(TONE_NOTIFY_NEUTRAL_TUNE);
	}
}

/**
 * Blink red LED and play negative tune (if use_buzzer == true).
 */
void tune_negative(bool use_buzzer)
{
	blink_msg_end = hrt_absolute_time() + BLINK_MSG_TIME;
	rgbled_set_color(RGBLED_COLOR_RED);
	rgbled_set_mode(RGBLED_MODE_BLINK_FAST);

	if (use_buzzer) {
		set_tune(TONE_NOTIFY_NEGATIVE_TUNE);
	}
}

int blink_msg_state()
{
	if (blink_msg_end == 0) {
		return 0;

	} else if (hrt_absolute_time() > blink_msg_end) {
		blink_msg_end = 0;
		return 2;

	} else {
		return 1;
	}
}

static int leds = -1;
static int rgbleds = -1;

int led_init()
{
	blink_msg_end = 0;

	/* first open normal LEDs */
	leds = open(LED0_DEVICE_PATH, 0);

	if (leds < 0) {
		warnx("LED: open fail\n");
		return ERROR;
	}

	/* the blue LED is only available on FMUv1 & AeroCore but not FMUv2 */
	(void)ioctl(leds, LED_ON, LED_BLUE);

	/* switch blue off */
	led_off(LED_BLUE);

	/* we consider the amber led mandatory */
	if (ioctl(leds, LED_ON, LED_AMBER)) {
		warnx("Amber LED: ioctl fail\n");
		return ERROR;
	}

	/* switch amber off */
	led_off(LED_AMBER);

	/* then try RGB LEDs, this can fail on FMUv1*/
	rgbleds = open(RGBLED0_DEVICE_PATH, 0);

	if (rgbleds < 0) {
		warnx("No RGB LED found at " RGBLED0_DEVICE_PATH);
	}

	return 0;
}

void led_deinit()
{
	if (leds >= 0) {
		close(leds);
	}

	if (rgbleds >= 0) {
		close(rgbleds);
	}
}

int led_toggle(int led)
{
	if (leds < 0) {
		return leds;
	}
	return ioctl(leds, LED_TOGGLE, led);
}

int led_on(int led)
{
	if (leds < 0) {
		return leds;
	}
	return ioctl(leds, LED_ON, led);
}

int led_off(int led)
{
	if (leds < 0) {
		return leds;
	}
	return ioctl(leds, LED_OFF, led);
}

void rgbled_set_color(rgbled_color_t color)
{

	if (rgbleds < 0) {
		return;
	}
	ioctl(rgbleds, RGBLED_SET_COLOR, (unsigned long)color);
}

void rgbled_set_mode(rgbled_mode_t mode)
{

	if (rgbleds < 0) {
		return;
	}
	ioctl(rgbleds, RGBLED_SET_MODE, (unsigned long)mode);
}

void rgbled_set_pattern(rgbled_pattern_t *pattern)
{

	if (rgbleds < 0) {
		return;
	}
	ioctl(rgbleds, RGBLED_SET_PATTERN, (unsigned long)pattern);
}

unsigned battery_get_n_cells() {
	return bat_n_cells;
}

float battery_remaining_estimate_voltage(float voltage, float discharged, float throttle_normalized)
{
	float ret = 0;

	if (counter % 100 == 0) {
		param_get(bat_v_empty_h, &bat_v_empty);
		param_get(bat_v_full_h, &bat_v_full);
		param_get(bat_v_load_drop_h, &bat_v_load_drop);
		param_get(bat_n_cells_h, &bat_n_cells);
		param_get(bat_capacity_h, &bat_capacity);
	}

	counter++;

	/* remaining charge estimate based on voltage and internal resistance (drop under load) */
	float bat_v_empty_dynamic = bat_v_empty - (bat_v_load_drop * throttle_normalized);
	/* the range from full to empty is the same for batteries under load and without load,
	 * since the voltage drop applies to both the full and empty state
	 */
	float voltage_range = (bat_v_full - bat_v_empty);
	float remaining_voltage = (voltage - (bat_n_cells * bat_v_empty_dynamic)) / (bat_n_cells * voltage_range);

	if (bat_capacity > 0.0f) {
		/* if battery capacity is known, use discharged current for estimate, but don't show more than voltage estimate */
		ret = fminf(remaining_voltage, 1.0f - discharged / bat_capacity);

	} else {
		/* else use voltage */
		ret = remaining_voltage;
	}

	/* limit to sane values */
	ret = (ret < 0.0f) ? 0.0f : ret;
	ret = (ret > 1.0f) ? 1.0f : ret;
	return ret;
}
<commit_msg>Commander: Low-pass battery throttle to better match battery dynamics<commit_after>/****************************************************************************
 *
 *   Copyright (c) 2013, 2014 PX4 Development Team. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 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 commander_helper.cpp
 * Commander helper functions implementations
 *
 * @author Thomas Gubler <thomasgubler@student.ethz.ch>
 * @author Julian Oes <julian@oes.ch>
 * @author Anton Babushkin <anton.babushkin@me.com>
 *
 */

#include <stdio.h>
#include <unistd.h>
#include <stdint.h>
#include <stdbool.h>
#include <fcntl.h>
#include <math.h>
#include <string.h>

#include <uORB/uORB.h>
#include <uORB/topics/vehicle_status.h>
#include <uORB/topics/actuator_controls.h>
#include <uORB/topics/vehicle_control_mode.h>
#include <systemlib/err.h>
#include <systemlib/param/param.h>
#include <drivers/drv_hrt.h>
#include <drivers/drv_tone_alarm.h>
#include <drivers/drv_led.h>
#include <drivers/drv_rgbled.h>

#include "commander_helper.h"

/* oddly, ERROR is not defined for c++ */
#ifdef ERROR
# undef ERROR
#endif
static const int ERROR = -1;

#define BLINK_MSG_TIME	700000	// 3 fast blinks

bool is_multirotor(const struct vehicle_status_s *current_status)
{
	return ((current_status->system_type == vehicle_status_s::VEHICLE_TYPE_QUADROTOR) ||
		(current_status->system_type == vehicle_status_s::VEHICLE_TYPE_HEXAROTOR) ||
		(current_status->system_type == vehicle_status_s::VEHICLE_TYPE_OCTOROTOR) ||
		(current_status->system_type == vehicle_status_s::VEHICLE_TYPE_TRICOPTER));
}

bool is_rotary_wing(const struct vehicle_status_s *current_status)
{
	return is_multirotor(current_status) || (current_status->system_type == vehicle_status_s::VEHICLE_TYPE_HELICOPTER)
	       || (current_status->system_type == vehicle_status_s::VEHICLE_TYPE_COAXIAL);
}

bool is_vtol(const struct vehicle_status_s * current_status) {
	return (current_status->system_type == vehicle_status_s::VEHICLE_TYPE_VTOL_DUOROTOR ||
		current_status->system_type == vehicle_status_s::VEHICLE_TYPE_VTOL_QUADROTOR ||
		current_status->system_type == vehicle_status_s::VEHICLE_TYPE_VTOL_HEXAROTOR ||
		current_status->system_type == vehicle_status_s::VEHICLE_TYPE_VTOL_OCTOROTOR);
}

static int buzzer = -1;
static hrt_abstime blink_msg_end = 0;	// end time for currently blinking LED message, 0 if no blink message
static hrt_abstime tune_end = 0;		// end time of currently played tune, 0 for repeating tunes or silence
static int tune_current = TONE_STOP_TUNE;		// currently playing tune, can be interrupted after tune_end
static unsigned int tune_durations[TONE_NUMBER_OF_TUNES];

static param_t bat_v_empty_h;
static param_t bat_v_full_h;
static param_t bat_n_cells_h;
static param_t bat_capacity_h;
static param_t bat_v_load_drop_h;
static float bat_v_empty = 3.4f;
static float bat_v_full = 4.2f;
static float bat_v_load_drop = 0.06f;
static int bat_n_cells = 3;
static float bat_capacity = -1.0f;
static unsigned int counter = 0;
static float throttle_lowpassed = 0.0f;

int battery_init()
{
	bat_v_empty_h = param_find("BAT_V_EMPTY");
	bat_v_full_h = param_find("BAT_V_CHARGED");
	bat_n_cells_h = param_find("BAT_N_CELLS");
	bat_capacity_h = param_find("BAT_CAPACITY");
	bat_v_load_drop_h = param_find("BAT_V_LOAD_DROP");

	return OK;
}

int buzzer_init()
{
	tune_end = 0;
	tune_current = 0;
	memset(tune_durations, 0, sizeof(tune_durations));
	tune_durations[TONE_NOTIFY_POSITIVE_TUNE] = 800000;
	tune_durations[TONE_NOTIFY_NEGATIVE_TUNE] = 900000;
	tune_durations[TONE_NOTIFY_NEUTRAL_TUNE] = 500000;
	tune_durations[TONE_ARMING_WARNING_TUNE] = 3000000;

	buzzer = open(TONEALARM0_DEVICE_PATH, O_WRONLY);

	if (buzzer < 0) {
		warnx("Buzzer: open fail\n");
		return ERROR;
	}

	return OK;
}

void buzzer_deinit()
{
	close(buzzer);
}

void set_tune_override(int tune)
{
	ioctl(buzzer, TONE_SET_ALARM, tune);
}

void set_tune(int tune)
{
	unsigned int new_tune_duration = tune_durations[tune];

	/* don't interrupt currently playing non-repeating tune by repeating */
	if (tune_end == 0 || new_tune_duration != 0 || hrt_absolute_time() > tune_end) {
		/* allow interrupting current non-repeating tune by the same tune */
		if (tune != tune_current || new_tune_duration != 0) {
			ioctl(buzzer, TONE_SET_ALARM, tune);
		}

		tune_current = tune;

		if (new_tune_duration != 0) {
			tune_end = hrt_absolute_time() + new_tune_duration;

		} else {
			tune_end = 0;
		}
	}
}

void tune_home_set(bool use_buzzer)
{
	blink_msg_end = hrt_absolute_time() + BLINK_MSG_TIME;
	rgbled_set_color(RGBLED_COLOR_GREEN);
	rgbled_set_mode(RGBLED_MODE_BLINK_FAST);

	if (use_buzzer) {
		set_tune(TONE_HOME_SET);
	}
}

void tune_mission_ok(bool use_buzzer)
{
	blink_msg_end = hrt_absolute_time() + BLINK_MSG_TIME;
	rgbled_set_color(RGBLED_COLOR_GREEN);
	rgbled_set_mode(RGBLED_MODE_BLINK_FAST);

	if (use_buzzer) {
		set_tune(TONE_NOTIFY_NEUTRAL_TUNE);
	}
}

void tune_mission_fail(bool use_buzzer)
{
	blink_msg_end = hrt_absolute_time() + BLINK_MSG_TIME;
	rgbled_set_color(RGBLED_COLOR_GREEN);
	rgbled_set_mode(RGBLED_MODE_BLINK_FAST);

	if (use_buzzer) {
		set_tune(TONE_NOTIFY_NEGATIVE_TUNE);
	}
}

/**
 * Blink green LED and play positive tune (if use_buzzer == true).
 */
void tune_positive(bool use_buzzer)
{
	blink_msg_end = hrt_absolute_time() + BLINK_MSG_TIME;
	rgbled_set_color(RGBLED_COLOR_GREEN);
	rgbled_set_mode(RGBLED_MODE_BLINK_FAST);

	if (use_buzzer) {
		set_tune(TONE_NOTIFY_POSITIVE_TUNE);
	}
}

/**
 * Blink white LED and play neutral tune (if use_buzzer == true).
 */
void tune_neutral(bool use_buzzer)
{
	blink_msg_end = hrt_absolute_time() + BLINK_MSG_TIME;
	rgbled_set_color(RGBLED_COLOR_WHITE);
	rgbled_set_mode(RGBLED_MODE_BLINK_FAST);

	if (use_buzzer) {
		set_tune(TONE_NOTIFY_NEUTRAL_TUNE);
	}
}

/**
 * Blink red LED and play negative tune (if use_buzzer == true).
 */
void tune_negative(bool use_buzzer)
{
	blink_msg_end = hrt_absolute_time() + BLINK_MSG_TIME;
	rgbled_set_color(RGBLED_COLOR_RED);
	rgbled_set_mode(RGBLED_MODE_BLINK_FAST);

	if (use_buzzer) {
		set_tune(TONE_NOTIFY_NEGATIVE_TUNE);
	}
}

int blink_msg_state()
{
	if (blink_msg_end == 0) {
		return 0;

	} else if (hrt_absolute_time() > blink_msg_end) {
		blink_msg_end = 0;
		return 2;

	} else {
		return 1;
	}
}

static int leds = -1;
static int rgbleds = -1;

int led_init()
{
	blink_msg_end = 0;

	/* first open normal LEDs */
	leds = open(LED0_DEVICE_PATH, 0);

	if (leds < 0) {
		warnx("LED: open fail\n");
		return ERROR;
	}

	/* the blue LED is only available on FMUv1 & AeroCore but not FMUv2 */
	(void)ioctl(leds, LED_ON, LED_BLUE);

	/* switch blue off */
	led_off(LED_BLUE);

	/* we consider the amber led mandatory */
	if (ioctl(leds, LED_ON, LED_AMBER)) {
		warnx("Amber LED: ioctl fail\n");
		return ERROR;
	}

	/* switch amber off */
	led_off(LED_AMBER);

	/* then try RGB LEDs, this can fail on FMUv1*/
	rgbleds = open(RGBLED0_DEVICE_PATH, 0);

	if (rgbleds < 0) {
		warnx("No RGB LED found at " RGBLED0_DEVICE_PATH);
	}

	return 0;
}

void led_deinit()
{
	if (leds >= 0) {
		close(leds);
	}

	if (rgbleds >= 0) {
		close(rgbleds);
	}
}

int led_toggle(int led)
{
	if (leds < 0) {
		return leds;
	}
	return ioctl(leds, LED_TOGGLE, led);
}

int led_on(int led)
{
	if (leds < 0) {
		return leds;
	}
	return ioctl(leds, LED_ON, led);
}

int led_off(int led)
{
	if (leds < 0) {
		return leds;
	}
	return ioctl(leds, LED_OFF, led);
}

void rgbled_set_color(rgbled_color_t color)
{

	if (rgbleds < 0) {
		return;
	}
	ioctl(rgbleds, RGBLED_SET_COLOR, (unsigned long)color);
}

void rgbled_set_mode(rgbled_mode_t mode)
{

	if (rgbleds < 0) {
		return;
	}
	ioctl(rgbleds, RGBLED_SET_MODE, (unsigned long)mode);
}

void rgbled_set_pattern(rgbled_pattern_t *pattern)
{

	if (rgbleds < 0) {
		return;
	}
	ioctl(rgbleds, RGBLED_SET_PATTERN, (unsigned long)pattern);
}

unsigned battery_get_n_cells() {
	return bat_n_cells;
}

float battery_remaining_estimate_voltage(float voltage, float discharged, float throttle_normalized)
{
	float ret = 0;

	if (counter % 100 == 0) {
		param_get(bat_v_empty_h, &bat_v_empty);
		param_get(bat_v_full_h, &bat_v_full);
		param_get(bat_v_load_drop_h, &bat_v_load_drop);
		param_get(bat_n_cells_h, &bat_n_cells);
		param_get(bat_capacity_h, &bat_capacity);
	}

	counter++;

	// XXX this time constant needs to become tunable
	// but really, the right fix are smart batteries.
	float val = throttle_lowpassed * 0.97f + throttle_normalized * 0.03f;
	if (isfinite(val)) {
		throttle_lowpassed = val;
	}

	/* remaining charge estimate based on voltage and internal resistance (drop under load) */
	float bat_v_empty_dynamic = bat_v_empty - (bat_v_load_drop * throttle_lowpassed);
	/* the range from full to empty is the same for batteries under load and without load,
	 * since the voltage drop applies to both the full and empty state
	 */
	float voltage_range = (bat_v_full - bat_v_empty);
	float remaining_voltage = (voltage - (bat_n_cells * bat_v_empty_dynamic)) / (bat_n_cells * voltage_range);

	if (bat_capacity > 0.0f) {
		/* if battery capacity is known, use discharged current for estimate, but don't show more than voltage estimate */
		ret = fminf(remaining_voltage, 1.0f - discharged / bat_capacity);

	} else {
		/* else use voltage */
		ret = remaining_voltage;
	}

	/* limit to sane values */
	ret = (ret < 0.0f) ? 0.0f : ret;
	ret = (ret > 1.0f) ? 1.0f : ret;
	return ret;
}
<|endoftext|>


<commit_before>/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
#ident "$Id$"
#ident "Copyright (c) 2007-2012 Tokutek Inc.  All rights reserved."
#ident "The technology is licensed by the Massachusetts Institute of Technology, Rutgers State University of New Jersey, and the Research Foundation of State University of New York at Stony Brook under United States of America Serial No. 11/760379 and to the patents and/or patent applications resulting from it."
#include <valgrind/helgrind.h>
#include <sys/types.h>
#include <pthread.h>

#include "memory.h"
#include "partitioned_counter.h"
#include "doubly_linked_list.h"
#include "growable_array.h"

//******************************************************************************

// Representation: The representation of a partitioned counter
//  comprises a sum, called sum_of_dead; an index, called the ckey,
//  which indexes into a thread-local array to find a thread-local
//  part of the counter; and a linked list of thread-local parts.
//  There is also a linked list, for each thread that has a
//  thread-local part of any counter, of all the thread-local parts of
//  all the counters.
// Abstraction function: The sum is represented by the sum of _sum and
//  the sum's of the thread-local parts of the counter.
// Representation invariant: Every thread-local part is in the linked
//  list of the thread-local parts of its counter, as well as in the
//  linked list of the counters of a the thread.
//******************************************************************************

//******************************************************************************
// The mutex for the PARTITIONED_COUNTER
// We have a single mutex for all the counters because
//  (a) the mutex is obtained infrequently, and
//  (b) it helps us avoid race conditions when destroying the counters.
// The alternative that I couldn't make work is to have a mutex per counter.
//   But the problem is that the counter can be destroyed before threads
//   terminate, or maybe a thread terminates before the counter is destroyed.
//   If the counter is destroyed first, then the mutex is no longer available.
//******************************************************************************

static pthread_mutex_t partitioned_counter_mutex = PTHREAD_MUTEX_INITIALIZER;

static void pc_lock (void)
// Effect: Lock the mutex.
{
    int r = pthread_mutex_lock(&partitioned_counter_mutex);
    assert(r==0);
}

static void pc_unlock (void)
// Effect: Unlock the mutex.
{
    int r = pthread_mutex_unlock(&partitioned_counter_mutex);
    assert(r==0);
}

//******************************************************************************
// Key creation primivites.
//******************************************************************************
static void pk_create (pthread_key_t *key, void (*destructor)(void*)) {
    int r = pthread_key_create(key, destructor);
    assert(r==0);
}

static void pk_delete (pthread_key_t key) {
    int r = pthread_key_delete(key);
    assert(r==0);
}

static void pk_setspecific (pthread_key_t key, const void *value) {
    int r = pthread_setspecific(key, value);
    assert(r==0);
}

//******************************************************************************
// The counter itself.
// The thread local part of a counter, comprising the thread-local sum a pointer
//  to the partitioned_counter, a pointer to the thread_local list head, and two
//  linked lists. One of the lists is all the thread-local parts that belong to
//  the same counter, and the other is all the thread-local parts that belogn to
//  the same thread.
//******************************************************************************

struct local_counter;

struct partitioned_counter {
    uint64_t       sum_of_dead;                                     // The sum of all thread-local counts from threads that have terminated.
    uint64_t       pc_key;                                          // A unique integer among all counters that have been created but not yet destroyed.
    DoublyLinkedList<struct local_counter *> ll_counter_head; // A linked list of all the thread-local information for this counter.
};

struct local_counter {
    uint64_t                                   sum;                // The thread-local sum.
    PARTITIONED_COUNTER                        owner_pc;           // The partitioned counter that this is part of.
    GrowableArray<struct local_counter *>     *thread_local_array; // The thread local array for this thread holds this local_counter at offset owner_pc->pc_key.
    LinkedListElement<struct local_counter *>  ll_in_counter;      // Element for the doubly-linked list of thread-local information for this PARTITIONED_COUNTER.
};

// Try to get it it into one cache line by aligning it.
static __thread GrowableArray<struct local_counter *> thread_local_array;

// I want this to be static, but I have to use hidden visibility instead because it's a friend function.
static void destroy_thread_local_part_of_partitioned_counters (void *ignore_me);
static void destroy_thread_local_part_of_partitioned_counters (void *ignore_me __attribute__((__unused__)))
// Effect: This function is called whenever a thread terminates using the
//  destructor of the thread_destructor_key (defined below).  First grab the
//  lock, then go through all the partitioned counters and removes the part that
//  is local to this thread.  We don't actually need the contents of the
//  thread_destructor_key except to cause this function to run.  The content of
//  the key is a static string, so don't try to free it.
{
    pc_lock();
    for (size_t i=0; i<thread_local_array.get_size(); i++) {
        struct local_counter *lc = thread_local_array.fetch_unchecked(i);
        if (lc==NULL) continue;
        PARTITIONED_COUNTER owner = lc->owner_pc;
        owner->sum_of_dead += lc->sum;
        owner->ll_counter_head.remove(&lc->ll_in_counter);
        toku_free(lc);
    }
    thread_local_array.deinit();
    pc_unlock();
}

//******************************************************************************
// We employ a system-wide pthread_key simply to get a notification when a
//  thread terminates. The key will simply contain a constant string (it's "dont
//  care", but it doesn't matter what it is, as long as it's not NULL.  We need
//  a constructor function to set up the pthread_key.  We used a constructor
//  function intead of a C++ constructor because that's what we are used to,
//  rather than because it's necessarily better.  Whenever a thread tries to
//  increment a partitioned_counter for the first time, it sets the
//  pthread_setspecific for the thread_destructor_key.  It's OK if the key gets
//  setspecific multiple times, it's always the same value.  When a thread (that
//  has created a thread-local part of any partitioned counter) terminates, the
//  destroy_thread_local_part_of_partitioned_counters will run.  It may run
//  before or after other pthread_key destructors, but the thread-local
//  ll_thread_head variable is still present until the thread is completely done
//  running.
//******************************************************************************

static pthread_key_t thread_destructor_key;

//******************************************************************************
// We don't like using up pthread_keys (macos provides only 128 of them),
// so we built our own.
//******************************************************************************

bool *counters_in_use = NULL;
uint64_t counters_in_use_size = 0;

static uint64_t allocate_counter (void)
// Effect: Find an unused counter number, and allocate it, returning the counter number.
// Requires: The pc mutex is held before calling.
{
    for (uint64_t i=0; i<counters_in_use_size; i++) {
        if (!counters_in_use[i]) {
            counters_in_use[i]=true;
            return i;
        }
    }
    uint64_t old_size = counters_in_use_size;
    if (counters_in_use_size==0) {
        counters_in_use_size = 1;
    } else {
        counters_in_use_size *= 2;
    }
    XREALLOC_N(counters_in_use_size, counters_in_use);
    for (uint64_t i=old_size; i<counters_in_use_size; i++) {
        counters_in_use[i] = false;
    }
    assert(old_size < counters_in_use_size);
    counters_in_use[old_size] = true;
    return old_size;
}

static void free_counter(uint64_t counternum)
// Effect: Free a counter.
// Requires: The pc mutex is held before calling.
{
    assert(counternum < counters_in_use_size);
    assert(counters_in_use[counternum]);
    counters_in_use[counternum] = false;
}

static void destroy_counters (void) {
    toku_free(counters_in_use);
    counters_in_use=NULL;
    counters_in_use_size=0;
}

//******************************************************************************
// Now for the code that actually creates a counter.
//******************************************************************************

PARTITIONED_COUNTER create_partitioned_counter(void)
// Effect: Create a counter, initialized to zero.
{
    PARTITIONED_COUNTER XMALLOC(result);
    result->sum_of_dead = 0;
    result->pc_key = allocate_counter();
    result->ll_counter_head.init();
    return result;
}

void destroy_partitioned_counter(PARTITIONED_COUNTER pc)
// Effect: Destroy the counter.  No operations on this counter are permitted after.
// Implementation note: Since we have a global lock, we can destroy all the thread-local
//  versions as well.
{
    pc_lock();
    uint64_t pc_key = pc->pc_key;
    LinkedListElement<struct local_counter *> *first;
    while (pc->ll_counter_head.pop(&first)) {
        // We just removed first from the counter list, now we must remove it from the thread-local array.
        struct local_counter *lc = first->get_container();
        assert(pc == lc->owner_pc);
        GrowableArray<struct local_counter *> *tla = lc->thread_local_array;
        tla->store_unchecked(pc_key, NULL);
        toku_free(lc);
    }
    toku_free(pc);
    free_counter(pc_key);
    pc_unlock();
}

static inline struct local_counter *get_thread_local_counter(uint64_t pc_key, GrowableArray<struct local_counter *> *a)
{
    if (pc_key >= a->get_size()) {
        return NULL;
    } else {
        return a->fetch_unchecked(pc_key);
    }
}

void increment_partitioned_counter(PARTITIONED_COUNTER pc, uint64_t amount)
// Effect: Increment the counter by amount.
// Requires: No overflows.  This is a 64-bit unsigned counter.
{
    // Only this thread is allowed to modify thread_local_array, except for setting tla->array[pc_key] to NULL
    // when a counter is destroyed (and in that case there should be no race because no other thread should be
    // trying to access the same local counter at the same time.
    uint64_t pc_key = pc->pc_key;
    struct local_counter *lc = get_thread_local_counter(pc_key, &thread_local_array);
    if (lc==NULL) {
        // Set things up so that this thread terminates, the thread-local parts of the counter will be destroyed and merged into their respective counters.
        pk_setspecific(thread_destructor_key, "dont care");

	XMALLOC(lc);
	lc->sum         = 0;
	HELGRIND_VALGRIND_HG_DISABLE_CHECKING(&lc->sum, sizeof(lc->sum)); // the counter increment is kind of racy.
	lc->owner_pc    = pc;
        lc->thread_local_array = &thread_local_array;

	pc_lock();  // Might as well do the malloc without holding the pc lock.  But the rest of this work needs the lock.

        // Grow the array if needed, filling in NULLs
        while (thread_local_array.get_size() <= pc_key) {
            thread_local_array.push(NULL);
        }
        thread_local_array.store_unchecked(pc_key, lc);
        pc->ll_counter_head.insert(&lc->ll_in_counter, lc);
	pc_unlock();
    }
    lc->sum += amount;
}

static int sumit(struct local_counter *lc, uint64_t *sum) {
    (*sum)+=lc->sum;
    return 0;
}

uint64_t read_partitioned_counter(PARTITIONED_COUNTER pc)
// Effect: Return the current value of the counter.
// Implementation note: Sum all the thread-local counts along with the sum_of_the_dead.
{
    pc_lock();
    uint64_t sum = pc->sum_of_dead;
    int r = pc->ll_counter_head.iterate<uint64_t *>(sumit, &sum);
    assert(r==0);
    pc_unlock();
    return sum;
}

void partitioned_counters_init(void)
// Effect: Initialize any partitioned counters data structures that must be set up before any partitioned counters run.
{
    pk_create(&thread_destructor_key, destroy_thread_local_part_of_partitioned_counters);
}

void partitioned_counters_destroy(void)
// Effect: Destroy any partitioned counters data structures.
{
    pk_delete(thread_destructor_key);
    destroy_counters();
}

<commit_msg>Fix #5321. Refs #5295, #5292, #5290. {{{svn merge -r46336:46350 ../tokudb.5321}}}<commit_after>/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
// vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
#ident "$Id$"
#ident "Copyright (c) 2007-2012 Tokutek Inc.  All rights reserved."
#ident "The technology is licensed by the Massachusetts Institute of Technology, Rutgers State University of New Jersey, and the Research Foundation of State University of New York at Stony Brook under United States of America Serial No. 11/760379 and to the patents and/or patent applications resulting from it."
#include <valgrind/helgrind.h>
#include <sys/types.h>
#include <pthread.h>

#include "memory.h"
#include "partitioned_counter.h"
#include "doubly_linked_list.h"
#include "growable_array.h"

//******************************************************************************
//
// Representation: The representation of a partitioned counter comprises a
//  sum, called sum_of_dead; an index, called the ckey, which indexes into a
//  thread-local array to find a thread-local part of the counter; and a
//  linked list of thread-local parts.
//
//  There is also a linked list, for each thread that has a thread-local part
//  of any counter, of all the thread-local parts of all the counters.
//
//  There is a pthread_key which gives us a hook to clean up thread-local
//  state when a thread terminates.  For each thread-local part of a counter
//  that the thread has, we add in the thread-local sum into the sum_of_dead.
//
//  Finally there is a list of all the thread-local arrays so that when we
//  destroy the partitioned counter before the threads are done, we can find
//  and destroy the thread_local_arrays before destroying the pthread_key.
//
// Abstraction function: The sum is represented by the sum of _sum and the
//  sum's of the thread-local parts of the counter.
//
// Representation invariant: Every thread-local part is in the linked list of
//  the thread-local parts of its counter, as well as in the linked list of
//  the counters of a the thread.
//
//******************************************************************************

//******************************************************************************
// The mutex for the PARTITIONED_COUNTER
// We have a single mutex for all the counters because
//  (a) the mutex is obtained infrequently, and
//  (b) it helps us avoid race conditions when destroying the counters.
// The alternative that I couldn't make work is to have a mutex per counter.
//   But the problem is that the counter can be destroyed before threads
//   terminate, or maybe a thread terminates before the counter is destroyed.
//   If the counter is destroyed first, then the mutex is no longer available.
//******************************************************************************

static pthread_mutex_t partitioned_counter_mutex = PTHREAD_MUTEX_INITIALIZER;

static void pc_lock (void)
// Effect: Lock the mutex.
{
    int r = pthread_mutex_lock(&partitioned_counter_mutex);
    assert(r==0);
}

static void pc_unlock (void)
// Effect: Unlock the mutex.
{
    int r = pthread_mutex_unlock(&partitioned_counter_mutex);
    assert(r==0);
}

//******************************************************************************
// Key creation primivites.
//******************************************************************************
static void pk_create (pthread_key_t *key, void (*destructor)(void*)) {
    int r = pthread_key_create(key, destructor);
    assert(r==0);
}

static void pk_delete (pthread_key_t key) {
    int r = pthread_key_delete(key);
    assert(r==0);
}

static void pk_setspecific (pthread_key_t key, const void *value) {
    int r = pthread_setspecific(key, value);
    assert(r==0);
}

//******************************************************************************
// The counter itself.
// The thread local part of a counter, comprising the thread-local sum a pointer
//  to the partitioned_counter, a pointer to the thread_local list head, and two
//  linked lists. One of the lists is all the thread-local parts that belong to
//  the same counter, and the other is all the thread-local parts that belogn to
//  the same thread.
//******************************************************************************

struct local_counter;

struct partitioned_counter {
    uint64_t       sum_of_dead;                                     // The sum of all thread-local counts from threads that have terminated.
    uint64_t       pc_key;                                          // A unique integer among all counters that have been created but not yet destroyed.
    DoublyLinkedList<struct local_counter *> ll_counter_head; // A linked list of all the thread-local information for this counter.
};

struct local_counter {
    uint64_t                                   sum;                // The thread-local sum.
    PARTITIONED_COUNTER                        owner_pc;           // The partitioned counter that this is part of.
    GrowableArray<struct local_counter *>     *thread_local_array; // The thread local array for this thread holds this local_counter at offset owner_pc->pc_key.
    LinkedListElement<struct local_counter *>  ll_in_counter;      // Element for the doubly-linked list of thread-local information for this PARTITIONED_COUNTER.
};

// Try to get it it into one cache line by aligning it.
static __thread GrowableArray<struct local_counter *> thread_local_array;
static __thread bool                                  thread_local_array_inited = false;

DoublyLinkedList<GrowableArray<struct local_counter *> *> all_thread_local_arrays;
__thread LinkedListElement<GrowableArray<struct local_counter *> *> thread_local_ll_elt;

// I want this to be static, but I have to use hidden visibility instead because it's a friend function.
static void destroy_thread_local_part_of_partitioned_counters (void *ignore_me);
static void destroy_thread_local_part_of_partitioned_counters (void *ignore_me __attribute__((__unused__)))
// Effect: This function is called whenever a thread terminates using the
//  destructor of the thread_destructor_key (defined below).  First grab the
//  lock, then go through all the partitioned counters and removes the part that
//  is local to this thread.  We don't actually need the contents of the
//  thread_destructor_key except to cause this function to run.  The content of
//  the key is a static string, so don't try to free it.
{
    pc_lock();
    for (size_t i=0; i<thread_local_array.get_size(); i++) {
        struct local_counter *lc = thread_local_array.fetch_unchecked(i);
        if (lc==NULL) continue;
        PARTITIONED_COUNTER owner = lc->owner_pc;
        owner->sum_of_dead += lc->sum;
        owner->ll_counter_head.remove(&lc->ll_in_counter);
        toku_free(lc);
    }
    all_thread_local_arrays.remove(&thread_local_ll_elt);
    thread_local_array_inited = false;
    thread_local_array.deinit();
    pc_unlock();
}

//******************************************************************************
// We employ a system-wide pthread_key simply to get a notification when a
//  thread terminates. The key will simply contain a constant string (it's "dont
//  care", but it doesn't matter what it is, as long as it's not NULL.  We need
//  a constructor function to set up the pthread_key.  We used a constructor
//  function intead of a C++ constructor because that's what we are used to,
//  rather than because it's necessarily better.  Whenever a thread tries to
//  increment a partitioned_counter for the first time, it sets the
//  pthread_setspecific for the thread_destructor_key.  It's OK if the key gets
//  setspecific multiple times, it's always the same value.  When a thread (that
//  has created a thread-local part of any partitioned counter) terminates, the
//  destroy_thread_local_part_of_partitioned_counters will run.  It may run
//  before or after other pthread_key destructors, but the thread-local
//  ll_thread_head variable is still present until the thread is completely done
//  running.
//******************************************************************************

static pthread_key_t thread_destructor_key;

//******************************************************************************
// We don't like using up pthread_keys (macos provides only 128 of them),
// so we built our own.
//******************************************************************************

bool *counters_in_use = NULL;
uint64_t counters_in_use_size = 0;

static uint64_t allocate_counter (void)
// Effect: Find an unused counter number, and allocate it, returning the counter number.
// Requires: The pc mutex is held before calling.
{
    for (uint64_t i=0; i<counters_in_use_size; i++) {
        if (!counters_in_use[i]) {
            counters_in_use[i]=true;
            return i;
        }
    }
    uint64_t old_size = counters_in_use_size;
    if (counters_in_use_size==0) {
        counters_in_use_size = 1;
    } else {
        counters_in_use_size *= 2;
    }
    XREALLOC_N(counters_in_use_size, counters_in_use);
    for (uint64_t i=old_size; i<counters_in_use_size; i++) {
        counters_in_use[i] = false;
    }
    assert(old_size < counters_in_use_size);
    counters_in_use[old_size] = true;
    return old_size;
}

static void free_counter(uint64_t counternum)
// Effect: Free a counter.
// Requires: The pc mutex is held before calling.
{
    assert(counternum < counters_in_use_size);
    assert(counters_in_use[counternum]);
    counters_in_use[counternum] = false;
}

static void destroy_counters (void) {
    toku_free(counters_in_use);
    counters_in_use=NULL;
    counters_in_use_size=0;
}

//******************************************************************************
// Now for the code that actually creates a counter.
//******************************************************************************

PARTITIONED_COUNTER create_partitioned_counter(void)
// Effect: Create a counter, initialized to zero.
{
    PARTITIONED_COUNTER XMALLOC(result);
    result->sum_of_dead = 0;
    result->pc_key = allocate_counter();
    result->ll_counter_head.init();
    return result;
}

void destroy_partitioned_counter(PARTITIONED_COUNTER pc)
// Effect: Destroy the counter.  No operations on this counter are permitted after.
// Implementation note: Since we have a global lock, we can destroy all the thread-local
//  versions as well.
{
    pc_lock();
    uint64_t pc_key = pc->pc_key;
    LinkedListElement<struct local_counter *> *first;
    while (pc->ll_counter_head.pop(&first)) {
        // We just removed first from the counter list, now we must remove it from the thread-local array.
        struct local_counter *lc = first->get_container();
        assert(pc == lc->owner_pc);
        GrowableArray<struct local_counter *> *tla = lc->thread_local_array;
        tla->store_unchecked(pc_key, NULL);
        toku_free(lc);
    }
    toku_free(pc);
    free_counter(pc_key);
    pc_unlock();
}

static inline struct local_counter *get_thread_local_counter(uint64_t pc_key, GrowableArray<struct local_counter *> *a)
{
    if (pc_key >= a->get_size()) {
        return NULL;
    } else {
        return a->fetch_unchecked(pc_key);
    }
}

void increment_partitioned_counter(PARTITIONED_COUNTER pc, uint64_t amount)
// Effect: Increment the counter by amount.
// Requires: No overflows.  This is a 64-bit unsigned counter.
{
    // Only this thread is allowed to modify thread_local_array, except for setting tla->array[pc_key] to NULL
    // when a counter is destroyed (and in that case there should be no race because no other thread should be
    // trying to access the same local counter at the same time.
    uint64_t pc_key = pc->pc_key;
    struct local_counter *lc = get_thread_local_counter(pc_key, &thread_local_array);
    if (lc==NULL) {
        // Set things up so that this thread terminates, the thread-local parts of the counter will be destroyed and merged into their respective counters.
        if (!thread_local_array_inited) {
            pk_setspecific(thread_destructor_key, "dont care");
            thread_local_array_inited=true;
            thread_local_array.init();
            all_thread_local_arrays.insert(&thread_local_ll_elt, &thread_local_array);
        }

	XMALLOC(lc);
	lc->sum         = 0;
	HELGRIND_VALGRIND_HG_DISABLE_CHECKING(&lc->sum, sizeof(lc->sum)); // the counter increment is kind of racy.
	lc->owner_pc    = pc;
        lc->thread_local_array = &thread_local_array;

	pc_lock();  // Might as well do the malloc without holding the pc lock.  But the rest of this work needs the lock.

        // Grow the array if needed, filling in NULLs
        while (thread_local_array.get_size() <= pc_key) {
            thread_local_array.push(NULL);
        }
        thread_local_array.store_unchecked(pc_key, lc);
        pc->ll_counter_head.insert(&lc->ll_in_counter, lc);
	pc_unlock();
    }
    lc->sum += amount;
}

static int sumit(struct local_counter *lc, uint64_t *sum) {
    (*sum)+=lc->sum;
    return 0;
}

uint64_t read_partitioned_counter(PARTITIONED_COUNTER pc)
// Effect: Return the current value of the counter.
// Implementation note: Sum all the thread-local counts along with the sum_of_the_dead.
{
    pc_lock();
    uint64_t sum = pc->sum_of_dead;
    int r = pc->ll_counter_head.iterate<uint64_t *>(sumit, &sum);
    assert(r==0);
    pc_unlock();
    return sum;
}

void partitioned_counters_init(void)
// Effect: Initialize any partitioned counters data structures that must be set up before any partitioned counters run.
{
    pk_create(&thread_destructor_key, destroy_thread_local_part_of_partitioned_counters);
    all_thread_local_arrays.init();
}

void partitioned_counters_destroy(void)
// Effect: Destroy any partitioned counters data structures.
{
    pc_lock();
    LinkedListElement<GrowableArray<struct local_counter *> *> *a_ll;
    while (all_thread_local_arrays.pop(&a_ll)) {
        a_ll->get_container()->deinit();
    }

    pk_delete(thread_destructor_key);
    destroy_counters();
    pc_unlock();
}

<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: preventduplicateinteraction.cxx,v $
 *
 *  $Revision: 1.5 $
 *
 *  last change: $Author: obo $ $Date: 2006-09-16 14:02:22 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_framework.hxx"

#include "interaction/preventduplicateinteraction.hxx"

//_________________________________________________________________________________________________________________
//  my own includes
//_________________________________________________________________________________________________________________

//_________________________________________________________________________________________________________________
//  interface includes
//_________________________________________________________________________________________________________________

#ifndef _COM_SUN_STAR_TASK_XINTERACTIONABORT_HPP_
#include <com/sun/star/task/XInteractionAbort.hpp>
#endif

#ifndef _COM_SUN_STAR_TASK_XINTERACTIONRETRY_HPP_
#include <com/sun/star/task/XInteractionRetry.hpp>
#endif

//_________________________________________________________________________________________________________________
//  other includes
//_________________________________________________________________________________________________________________

//_________________________________________________________________________________________________________________
//  namespace
//_________________________________________________________________________________________________________________

namespace framework{

namespace css = ::com::sun::star;

//_________________________________________________________________________________________________________________
//  exported const
//_________________________________________________________________________________________________________________

#define IMPLEMENTATIONNAME_UIINTERACTIONHANDLER                 ::rtl::OUString::createFromAscii("com.sun.star.comp.uui.UUIInteractionHandler")

//_________________________________________________________________________________________________________________
//  exported definitions
//_________________________________________________________________________________________________________________

//_________________________________________________________________________________________________________________

PreventDuplicateInteraction::PreventDuplicateInteraction(const css::uno::Reference< css::lang::XMultiServiceFactory >& xSMGR)
    : ThreadHelpBase2()
    , m_xSMGR(xSMGR)
{
}

//_________________________________________________________________________________________________________________

PreventDuplicateInteraction::~PreventDuplicateInteraction()
{
}

//_________________________________________________________________________________________________________________

void PreventDuplicateInteraction::setHandler(const css::uno::Reference< css::task::XInteractionHandler >& xHandler)
{
    // SAFE ->
    ::osl::ResettableMutexGuard aLock(m_aLock);
    m_xHandler = xHandler;
    aLock.clear();
    // <- SAFE
}

//_________________________________________________________________________________________________________________

void PreventDuplicateInteraction::useDefaultUUIHandler()
{
    // SAFE ->
    ::osl::ResettableMutexGuard aLock(m_aLock);
    css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR;
    aLock.clear();
    // <- SAFE

    css::uno::Reference< css::task::XInteractionHandler > xHandler(
                            xSMGR->createInstance(IMPLEMENTATIONNAME_UIINTERACTIONHANDLER),
                            css::uno::UNO_QUERY_THROW);

    // SAFE ->
    aLock.reset();
    m_xHandler = xHandler;
    aLock.clear();
    // <- SAFE
}

//_________________________________________________________________________________________________________________

void SAL_CALL PreventDuplicateInteraction::handle(const css::uno::Reference< css::task::XInteractionRequest >& xRequest)
    throw(css::uno::RuntimeException)
{
    css::uno::Any aRequest  = xRequest->getRequest();
    sal_Bool      bHandleIt = sal_True;

    // SAFE ->
    ::osl::ResettableMutexGuard aLock(m_aLock);

    InteractionList::iterator pIt;
    for (  pIt  = m_lInteractionRules.begin();
           pIt != m_lInteractionRules.end()  ;
         ++pIt                               )
    {
        InteractionInfo& rInfo = *pIt;

        if (aRequest.isExtractableTo(rInfo.m_aInteraction))
        {
            ++rInfo.m_nCallCount;
            rInfo.m_xRequest = xRequest;
            bHandleIt = (rInfo.m_nCallCount <= rInfo.m_nMaxCount);
            break;
        }
    }

    css::uno::Reference< css::task::XInteractionHandler > xHandler = m_xHandler;

    aLock.clear();
    // <- SAFE

    if (
        (bHandleIt    ) &&
        (xHandler.is())
       )
    {
        xHandler->handle(xRequest);
    }
    else
    {
        const css::uno::Sequence< css::uno::Reference< css::task::XInteractionContinuation > > lContinuations = xRequest->getContinuations();
        sal_Int32 c = lContinuations.getLength();
        sal_Int32 i = 0;
        for (i=0; i<c; ++i)
        {
            css::uno::Reference< css::task::XInteractionAbort > xAbort(lContinuations[i], css::uno::UNO_QUERY);
            if (xAbort.is())
            {
                xAbort->select();
                break;
            }
        }
    }
}

//_________________________________________________________________________________________________________________

void PreventDuplicateInteraction::addInteractionRule(const PreventDuplicateInteraction::InteractionInfo& aInteractionInfo)
{
    // SAFE ->
    ::osl::ResettableMutexGuard aLock(m_aLock);

    InteractionList::iterator pIt;
    for (  pIt  = m_lInteractionRules.begin();
           pIt != m_lInteractionRules.end()  ;
         ++pIt                               )
    {
        InteractionInfo& rInfo = *pIt;
        if (rInfo.m_aInteraction == aInteractionInfo.m_aInteraction)
        {
            rInfo.m_nMaxCount  = aInteractionInfo.m_nMaxCount ;
            rInfo.m_nCallCount = aInteractionInfo.m_nCallCount;
            return;
        }
    }

    m_lInteractionRules.push_back(aInteractionInfo);

    aLock.clear();
    // <- SAFE
}

//_________________________________________________________________________________________________________________

sal_Bool PreventDuplicateInteraction::getInteractionInfo(const css::uno::Type&                               aInteraction,
                                                               PreventDuplicateInteraction::InteractionInfo* pReturn     ) const
{
    // SAFE ->
    ::osl::ResettableMutexGuard aLock(m_aLock);

    PreventDuplicateInteraction::InteractionList::const_iterator pIt;
    for (  pIt  = m_lInteractionRules.begin();
           pIt != m_lInteractionRules.end()  ;
         ++pIt                               )
    {
        const PreventDuplicateInteraction::InteractionInfo& rInfo = *pIt;
        if (rInfo.m_aInteraction == aInteraction)
        {
            *pReturn = rInfo;
            return sal_True;
        }
    }

    aLock.clear();
    // <- SAFE

    return sal_False;
}

} // namespace framework
<commit_msg>INTEGRATION: CWS changefileheader (1.5.262); FILE MERGED 2008/04/01 10:58:10 thb 1.5.262.2: #i85898# Stripping all external header guards 2008/03/28 15:35:21 rt 1.5.262.1: #i87441# Change license header to LPGL v3.<commit_after>/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2008 by Sun Microsystems, Inc.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * $RCSfile: preventduplicateinteraction.cxx,v $
 * $Revision: 1.6 $
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_framework.hxx"

#include "interaction/preventduplicateinteraction.hxx"

//_________________________________________________________________________________________________________________
//  my own includes
//_________________________________________________________________________________________________________________

//_________________________________________________________________________________________________________________
//  interface includes
//_________________________________________________________________________________________________________________
#include <com/sun/star/task/XInteractionAbort.hpp>
#include <com/sun/star/task/XInteractionRetry.hpp>

//_________________________________________________________________________________________________________________
//  other includes
//_________________________________________________________________________________________________________________

//_________________________________________________________________________________________________________________
//  namespace
//_________________________________________________________________________________________________________________

namespace framework{

namespace css = ::com::sun::star;

//_________________________________________________________________________________________________________________
//  exported const
//_________________________________________________________________________________________________________________

#define IMPLEMENTATIONNAME_UIINTERACTIONHANDLER                 ::rtl::OUString::createFromAscii("com.sun.star.comp.uui.UUIInteractionHandler")

//_________________________________________________________________________________________________________________
//  exported definitions
//_________________________________________________________________________________________________________________

//_________________________________________________________________________________________________________________

PreventDuplicateInteraction::PreventDuplicateInteraction(const css::uno::Reference< css::lang::XMultiServiceFactory >& xSMGR)
    : ThreadHelpBase2()
    , m_xSMGR(xSMGR)
{
}

//_________________________________________________________________________________________________________________

PreventDuplicateInteraction::~PreventDuplicateInteraction()
{
}

//_________________________________________________________________________________________________________________

void PreventDuplicateInteraction::setHandler(const css::uno::Reference< css::task::XInteractionHandler >& xHandler)
{
    // SAFE ->
    ::osl::ResettableMutexGuard aLock(m_aLock);
    m_xHandler = xHandler;
    aLock.clear();
    // <- SAFE
}

//_________________________________________________________________________________________________________________

void PreventDuplicateInteraction::useDefaultUUIHandler()
{
    // SAFE ->
    ::osl::ResettableMutexGuard aLock(m_aLock);
    css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR;
    aLock.clear();
    // <- SAFE

    css::uno::Reference< css::task::XInteractionHandler > xHandler(
                            xSMGR->createInstance(IMPLEMENTATIONNAME_UIINTERACTIONHANDLER),
                            css::uno::UNO_QUERY_THROW);

    // SAFE ->
    aLock.reset();
    m_xHandler = xHandler;
    aLock.clear();
    // <- SAFE
}

//_________________________________________________________________________________________________________________

void SAL_CALL PreventDuplicateInteraction::handle(const css::uno::Reference< css::task::XInteractionRequest >& xRequest)
    throw(css::uno::RuntimeException)
{
    css::uno::Any aRequest  = xRequest->getRequest();
    sal_Bool      bHandleIt = sal_True;

    // SAFE ->
    ::osl::ResettableMutexGuard aLock(m_aLock);

    InteractionList::iterator pIt;
    for (  pIt  = m_lInteractionRules.begin();
           pIt != m_lInteractionRules.end()  ;
         ++pIt                               )
    {
        InteractionInfo& rInfo = *pIt;

        if (aRequest.isExtractableTo(rInfo.m_aInteraction))
        {
            ++rInfo.m_nCallCount;
            rInfo.m_xRequest = xRequest;
            bHandleIt = (rInfo.m_nCallCount <= rInfo.m_nMaxCount);
            break;
        }
    }

    css::uno::Reference< css::task::XInteractionHandler > xHandler = m_xHandler;

    aLock.clear();
    // <- SAFE

    if (
        (bHandleIt    ) &&
        (xHandler.is())
       )
    {
        xHandler->handle(xRequest);
    }
    else
    {
        const css::uno::Sequence< css::uno::Reference< css::task::XInteractionContinuation > > lContinuations = xRequest->getContinuations();
        sal_Int32 c = lContinuations.getLength();
        sal_Int32 i = 0;
        for (i=0; i<c; ++i)
        {
            css::uno::Reference< css::task::XInteractionAbort > xAbort(lContinuations[i], css::uno::UNO_QUERY);
            if (xAbort.is())
            {
                xAbort->select();
                break;
            }
        }
    }
}

//_________________________________________________________________________________________________________________

void PreventDuplicateInteraction::addInteractionRule(const PreventDuplicateInteraction::InteractionInfo& aInteractionInfo)
{
    // SAFE ->
    ::osl::ResettableMutexGuard aLock(m_aLock);

    InteractionList::iterator pIt;
    for (  pIt  = m_lInteractionRules.begin();
           pIt != m_lInteractionRules.end()  ;
         ++pIt                               )
    {
        InteractionInfo& rInfo = *pIt;
        if (rInfo.m_aInteraction == aInteractionInfo.m_aInteraction)
        {
            rInfo.m_nMaxCount  = aInteractionInfo.m_nMaxCount ;
            rInfo.m_nCallCount = aInteractionInfo.m_nCallCount;
            return;
        }
    }

    m_lInteractionRules.push_back(aInteractionInfo);

    aLock.clear();
    // <- SAFE
}

//_________________________________________________________________________________________________________________

sal_Bool PreventDuplicateInteraction::getInteractionInfo(const css::uno::Type&                               aInteraction,
                                                               PreventDuplicateInteraction::InteractionInfo* pReturn     ) const
{
    // SAFE ->
    ::osl::ResettableMutexGuard aLock(m_aLock);

    PreventDuplicateInteraction::InteractionList::const_iterator pIt;
    for (  pIt  = m_lInteractionRules.begin();
           pIt != m_lInteractionRules.end()  ;
         ++pIt                               )
    {
        const PreventDuplicateInteraction::InteractionInfo& rInfo = *pIt;
        if (rInfo.m_aInteraction == aInteraction)
        {
            *pReturn = rInfo;
            return sal_True;
        }
    }

    aLock.clear();
    // <- SAFE

    return sal_False;
}

} // namespace framework
<|endoftext|>


<commit_before>/***********************************************************************************************************************
 **
 ** Copyright (c) 2011, 2016 ETH Zurich
 ** All rights reserved.
 **
 ** Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
 ** following conditions are met:
 **
 **    * Redistributions of source code must retain the above copyright notice, this list of conditions and the
 **      following disclaimer.
 **    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
 **      following disclaimer in the documentation and/or other materials provided with the distribution.
 **    * Neither the name of the ETH Zurich 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 ** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 ** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 **
 **********************************************************************************************************************/

#include "DiffManager.h"
#include "VersionControlUIException.h"

#include "VisualizationBase/src/VisualizationManager.h"
#include "VisualizationBase/src/items/ViewItem.h"
#include "VisualizationBase/src/nodes/ViewItemNode.h"
#include "VisualizationBase/src/ViewItemManager.h"
#include "VisualizationBase/src/declarative/GridLayouter.h"
#include "VisualizationBase/src/overlays/HighlightOverlay.h"
#include "VisualizationBase/src/overlays/ArrowOverlay.h"
#include "VisualizationBase/src/views/MainView.h"

#include "FilePersistence/src/version_control/GitRepository.h"
#include "FilePersistence/src/simple/SimpleTextFileStore.h"

#include "OOModel/src/declarations/Project.h"
#include "OOModel/src/statements/ExpressionStatement.h"
#include "OOModel/src/expressions/EmptyExpression.h"

#include "ModelBase/src/model/AllTreeManagers.h"
#include "ModelBase/src/model/TreeManager.h"
#include "ModelBase/src/nodes/Reference.h"

#include "nodes/DiffComparisonPair.h"

namespace VersionControlUI
{

struct ChangeWithNodes {
	Model::Node* oldNode_{};
	Model::Node* newNode_{};
	FilePersistence::ChangeType changeType_{FilePersistence::ChangeType::Unclassified};
};

struct DiffSetup {
	Model::TreeManager* newVersionManager_{};
	Model::TreeManager* oldVersionManager_{};
	FilePersistence::GitRepository* repository_{};
};

DiffManager::DiffManager(QString oldVersion, QString newVersion, QString project,
								 Model::SymbolMatcher contextUnitMatcher) :
	oldVersion_{oldVersion}, newVersion_{newVersion}, project_{project}, contextUnitMatcher_{contextUnitMatcher}
{}

DiffSetup DiffManager::initializeDiffPrerequisites()
{
	DiffSetup diffSetup{};

	QString projectsDir = "projects/" + project_;

	// get GitRepository
	if (!FilePersistence::GitRepository::repositoryExists(projectsDir))
		throw VersionControlUIException{"Diff setup not possible. No repository found at " + projectsDir};

	diffSetup.repository_ = new FilePersistence::GitRepository{projectsDir};;

	// load newer version
	diffSetup.newVersionManager_ = createTreeManagerFromVersion(diffSetup.repository_, newVersion_);

	// load older version
	diffSetup.oldVersionManager_ = createTreeManagerFromVersion(diffSetup.repository_, oldVersion_);

	Model::Reference::resolvePending();

	return diffSetup;

}

void DiffManager::computeChangeNodesAndNodesToVisualize(FilePersistence::IdToChangeDescriptionHash changes,
		QList<ChangeWithNodes>& changesWithNodes, QSet<Model::NodeIdType>& changedNodesToVisualize, DiffSetup& diffSetup)
{
	for (auto change : changes.values())
	{
		// TODO check flags
		if (change->isFake() || change->onlyStructureChange()) continue;

		auto id = change->nodeId();

		changesWithNodes.append({const_cast<Model::Node*>(diffSetup.oldVersionManager_->nodeIdMap().node(id)),
														 const_cast<Model::Node*>(diffSetup.newVersionManager_->nodeIdMap().node(id)),
														 change->type()});

		Model::NodeIdType nodeId;

		// TODO maybe use node instead of id
		if (change->type() != FilePersistence::ChangeType::Deletion)
			if (findChangedNode(diffSetup.newVersionManager_, id, nodeId))
				changedNodesToVisualize.insert(nodeId);
		if (change->type() != FilePersistence::ChangeType::Insertion)
			if (findChangedNode(diffSetup.oldVersionManager_, id, nodeId))
				changedNodesToVisualize.insert(nodeId);
	}

	removeDirectChildrenOfNodesInContainer(&changesWithNodes);
}

void DiffManager::visualize()
{
	auto diffSetup = initializeDiffPrerequisites();

	FilePersistence::Diff diff = diffSetup.repository_->diff(oldVersion_, newVersion_);

	auto changes = diff.changes();

	// detailed changes
	QList<ChangeWithNodes> changesWithNodes;

	// contains the nodes which will be drawn
	QSet<Model::NodeIdType> changedNodesToVisualize;

	// fill up lists
	computeChangeNodesAndNodesToVisualize(changes, changesWithNodes, changedNodesToVisualize, diffSetup);

	Visualization::VisualizationManager::instance().mainScene()->listenToTreeManager(diffSetup.newVersionManager_);
	Visualization::VisualizationManager::instance().mainScene()->listenToTreeManager(diffSetup.oldVersionManager_);

	auto diffViewItem = Visualization::VisualizationManager::instance().mainScene()->
			viewItems()->viewItem("DiffView");

	if (diffViewItem)
		Visualization::VisualizationManager::instance().mainScene()->
					viewItems()->removeViewItem(diffViewItem);

	diffViewItem = Visualization::VisualizationManager::instance().mainScene()->
				viewItems()->newViewItem("DiffView");

	diffViewItem->setMajorAxis(Visualization::GridLayouter::NoMajor);
	diffViewItem->setZoomLabelsEnabled(false);

	for (auto diffComparisonPair : createDiffComparisonPairs(diffSetup, changedNodesToVisualize))
		diffViewItem->insertNode(diffComparisonPair);

	// create visualization for changes
	Visualization::VisualizationManager::instance().mainScene()->addPostEventAction(
								  [diffViewItem, changesWithNodes]() {
		createOverlaysForChanges(diffViewItem, changesWithNodes);
	});

	// switch to the newly created view
	Visualization::VisualizationManager::instance().mainScene()->viewItems()->switchToView(diffViewItem);
}

// TODO maybe better implementation for this
void DiffManager::removeDirectChildrenOfNodesInContainer(QList<ChangeWithNodes>* container)
{
	QHash<Model::Node*, FilePersistence::ChangeType> oldNodes;
	QHash<Model::Node*, FilePersistence::ChangeType> newNodes;

	for (auto change : *container)
	{
		if (change.oldNode_)
			oldNodes.insert(change.oldNode_, change.changeType_);
		if (change.newNode_)
			newNodes.insert(change.newNode_, change.changeType_);
	}

	QSet<Model::Node*> oldNodesMarkedForRemoval = findAllNodesWithDirectParentPresent(oldNodes);
	QSet<Model::Node*> newNodesMarkedForRemoval = findAllNodesWithDirectParentPresent(newNodes);

	auto it = container->begin();
	while (it != container->end())
	{
		if (oldNodesMarkedForRemoval.contains(it->oldNode_) || newNodesMarkedForRemoval.contains(it->newNode_))
			it = container->erase(it);
		else
			it++;
	}
}

QSet<Model::Node*> DiffManager::findAllNodesWithDirectParentPresent(QHash<Model::Node*,
																						  FilePersistence::ChangeType>& nodes)
{
	QSet<Model::Node*> result;
	for (auto node : nodes.keys())
		// check that changeType also matches
		if (nodes.contains(node->parent()) && nodes[node] == nodes[node->parent()])
				result.insert(node);

	return result;
}

QSet<Visualization::Item*> DiffManager::findAllItemsWithAncestorsIn(QSet<Visualization::Item*> items)
{
	QSet<Visualization::Item*> result;
	for (auto item : items)
	{
		auto parent = item->parent();
		while (parent)
		{
			if (items.contains(parent))
			{
				result.insert(item);
				break;
			}
			parent = parent->parent();
		}
	}

	return result;
}

// TODO find good way to return and use Node instead of Id
bool DiffManager::findChangedNode(Model::TreeManager* treeManager, Model::NodeIdType id, Model::NodeIdType& resultId)
{
	if (auto node = const_cast<Model::Node*>(treeManager->nodeIdMap().node(id)))
	{

		Model::Node* changedNode = nullptr;

		if (auto ancestorNode = node->firstAncestorOfType(contextUnitMatcher_))
		{
			changedNode = ancestorNode;

			resultId = treeManager->nodeIdMap().id(changedNode);
			return true;
		}
		else
		{
			resultId = treeManager->nodeIdMap().id(node);
		}
		return true;
	}
	return false;
}

void DiffManager::createOverlaysForChanges(Visualization::ViewItem* diffViewItem,
														 QList<ChangeWithNodes> changesWithNodes)
{
	static const QString arrowLayer = "move_arrows";
	diffViewItem->setArrowStyle(arrowLayer, "thick");

	QSet<Visualization::Item*> allItemsToScale;
	for (auto change : changesWithNodes)
	{
		QString highlightOverlayStyle;
		QString highlightOverlayName;

		switch (change.changeType_)
		{
			case FilePersistence::ChangeType::Deletion:
				highlightOverlayName = "delete_highlights";
				highlightOverlayStyle = "delete_no_bg_solid_outline";
				break;
			case FilePersistence::ChangeType::Insertion:
				highlightOverlayName = "insert_highlights";
				highlightOverlayStyle = "insert_no_bg_solid_outline";
				break;
			case FilePersistence::ChangeType::Move:
				highlightOverlayName = "move_highlights";
				highlightOverlayStyle = "move_no_bg_solid_outline";
				break;
			case FilePersistence::ChangeType::Stationary:
				highlightOverlayName = "modify_highlights";
				highlightOverlayStyle = "modify_no_bg_solid_outline";
				break;
			case FilePersistence::ChangeType::Unclassified:
				Q_ASSERT(false);
				break;
		}

		Visualization::Item* oldNodeItem = addHighlightAndReturnItem(change.oldNode_, diffViewItem,
																						highlightOverlayName, highlightOverlayStyle);;
		Visualization::Item* newNodeItem = addHighlightAndReturnItem(change.newNode_, diffViewItem,
																						highlightOverlayName, highlightOverlayStyle);;

		if (oldNodeItem)
			allItemsToScale.insert(oldNodeItem);

		if (newNodeItem)
			allItemsToScale.insert(newNodeItem);

		if (change.changeType_== FilePersistence::ChangeType::Move)
		{
			auto overlay = new Visualization::ArrowOverlay{oldNodeItem, newNodeItem,
					  Visualization::ArrowOverlay::itemStyles().get("thick")};
			diffViewItem->addOverlay(overlay, arrowLayer);
		}

	}

	QSet<Visualization::Item*> removeItems = findAllItemsWithAncestorsIn(allItemsToScale);

	allItemsToScale.subtract(removeItems);

	Visualization::VisualizationManager::instance().mainScene()->
			addOnZoomHandler([allItemsToScale](qreal factor)
	{
		for (auto item : allItemsToScale)
		{
			if (factor >= 1.0)
				item->setScale(1.0);
			else if (factor >= 0.05)
				item->setScale((1/factor));
			else
				item->setScale((1/factor) * std::pow(0.95, 1/factor));
		}
	});

	// set zoom level further out and center the scene
	Visualization::VisualizationManager::instance().mainView()->zoom(7);
	Visualization::VisualizationManager::instance().mainView()->
			centerOn(Visualization::VisualizationManager::instance().mainView()->sceneRect().center());
}

Visualization::Item* DiffManager::addHighlightAndReturnItem(Model::Node* node, Visualization::ViewItem* viewItem,
                                               QString highlightOverlayName, QString highlightOverlayStyle)
{
	Visualization::Item* resultItem = nullptr;
	if (node)
	{
		if ((resultItem = viewItem->findVisualizationOf(node)))
		{
			auto overlay = new Visualization::HighlightOverlay{resultItem,
					Visualization::HighlightOverlay::itemStyles().get(highlightOverlayStyle)};
			resultItem->addOverlay(overlay, highlightOverlayName);
		}
		else if (auto parent = DCast<Model::CompositeNode>(node->parent()))
		{
			auto index = parent->indexOf(node);
			if (!parent->meta().attribute(index).name().startsWith("_"))
				return addHighlightAndReturnItem(node->parent(), viewItem, highlightOverlayName, highlightOverlayStyle);
		}
	}
	return resultItem;
}

QList<DiffComparisonPair*> DiffManager::createDiffComparisonPairs(DiffSetup& diffSetup,
													 QSet<Model::NodeIdType> diffComparisonPairNodeIds)
{
	QList<DiffComparisonPair*> diffComparisonPairs;
	for (auto id : diffComparisonPairNodeIds)
	{
		auto oldNode = const_cast<Model::Node*>(diffSetup.oldVersionManager_->nodeIdMap().node(id));
		auto newNode = const_cast<Model::Node*>(diffSetup.newVersionManager_->nodeIdMap().node(id));

		auto diffNode = new DiffComparisonPair{oldNode, newNode};

		diffComparisonPairs.append(diffNode);
	}
	return diffComparisonPairs;
}

Model::TreeManager* DiffManager::createTreeManagerFromVersion(FilePersistence::GitRepository* repository,
																				  QString version)
{
	if (version == FilePersistence::GitRepository::WORKDIR)
	{
		auto fileStore = new FilePersistence::SimpleTextFileStore {"projects/"};
		Model::TreeManager* versionTreeManager = new Model::TreeManager{};
		versionTreeManager->load(fileStore, project_, false);

		return versionTreeManager;

	}

	std::unique_ptr<const FilePersistence::Commit> commit{repository->getCommit(version)};

	auto fileStore = new FilePersistence::SimpleTextFileStore {
				[this, &commit](QString filename, const char*& data, int& size)
				{ return commit->getFileContent(filename, data, size, false); }
			};

	Model::TreeManager* versionTreeManager = new Model::TreeManager{};
	versionTreeManager->load(fileStore, project_, false);

	return versionTreeManager;
}

}
<commit_msg>Move diffSetup declaration to first use<commit_after>/***********************************************************************************************************************
 **
 ** Copyright (c) 2011, 2016 ETH Zurich
 ** All rights reserved.
 **
 ** Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
 ** following conditions are met:
 **
 **    * Redistributions of source code must retain the above copyright notice, this list of conditions and the
 **      following disclaimer.
 **    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
 **      following disclaimer in the documentation and/or other materials provided with the distribution.
 **    * Neither the name of the ETH Zurich 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 HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 ** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 ** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 **
 **********************************************************************************************************************/

#include "DiffManager.h"
#include "VersionControlUIException.h"

#include "VisualizationBase/src/VisualizationManager.h"
#include "VisualizationBase/src/items/ViewItem.h"
#include "VisualizationBase/src/nodes/ViewItemNode.h"
#include "VisualizationBase/src/ViewItemManager.h"
#include "VisualizationBase/src/declarative/GridLayouter.h"
#include "VisualizationBase/src/overlays/HighlightOverlay.h"
#include "VisualizationBase/src/overlays/ArrowOverlay.h"
#include "VisualizationBase/src/views/MainView.h"

#include "FilePersistence/src/version_control/GitRepository.h"
#include "FilePersistence/src/simple/SimpleTextFileStore.h"

#include "OOModel/src/declarations/Project.h"
#include "OOModel/src/statements/ExpressionStatement.h"
#include "OOModel/src/expressions/EmptyExpression.h"

#include "ModelBase/src/model/AllTreeManagers.h"
#include "ModelBase/src/model/TreeManager.h"
#include "ModelBase/src/nodes/Reference.h"

#include "nodes/DiffComparisonPair.h"

namespace VersionControlUI
{

struct ChangeWithNodes {
	Model::Node* oldNode_{};
	Model::Node* newNode_{};
	FilePersistence::ChangeType changeType_{FilePersistence::ChangeType::Unclassified};
};

struct DiffSetup {
	Model::TreeManager* newVersionManager_{};
	Model::TreeManager* oldVersionManager_{};
	FilePersistence::GitRepository* repository_{};
};

DiffManager::DiffManager(QString oldVersion, QString newVersion, QString project,
								 Model::SymbolMatcher contextUnitMatcher) :
	oldVersion_{oldVersion}, newVersion_{newVersion}, project_{project}, contextUnitMatcher_{contextUnitMatcher}
{}

DiffSetup DiffManager::initializeDiffPrerequisites()
{

	QString projectsDir = "projects/" + project_;

	// get GitRepository
	if (!FilePersistence::GitRepository::repositoryExists(projectsDir))
		throw VersionControlUIException{"Diff setup not possible. No repository found at " + projectsDir};

	DiffSetup diffSetup{};
	diffSetup.repository_ = new FilePersistence::GitRepository{projectsDir};;

	// load newer version
	diffSetup.newVersionManager_ = createTreeManagerFromVersion(diffSetup.repository_, newVersion_);

	// load older version
	diffSetup.oldVersionManager_ = createTreeManagerFromVersion(diffSetup.repository_, oldVersion_);

	Model::Reference::resolvePending();

	return diffSetup;

}

void DiffManager::computeChangeNodesAndNodesToVisualize(FilePersistence::IdToChangeDescriptionHash changes,
		QList<ChangeWithNodes>& changesWithNodes, QSet<Model::NodeIdType>& changedNodesToVisualize, DiffSetup& diffSetup)
{
	for (auto change : changes.values())
	{
		// TODO check flags
		if (change->isFake() || change->onlyStructureChange()) continue;

		auto id = change->nodeId();

		changesWithNodes.append({const_cast<Model::Node*>(diffSetup.oldVersionManager_->nodeIdMap().node(id)),
														 const_cast<Model::Node*>(diffSetup.newVersionManager_->nodeIdMap().node(id)),
														 change->type()});

		Model::NodeIdType nodeId;

		// TODO maybe use node instead of id
		if (change->type() != FilePersistence::ChangeType::Deletion)
			if (findChangedNode(diffSetup.newVersionManager_, id, nodeId))
				changedNodesToVisualize.insert(nodeId);
		if (change->type() != FilePersistence::ChangeType::Insertion)
			if (findChangedNode(diffSetup.oldVersionManager_, id, nodeId))
				changedNodesToVisualize.insert(nodeId);
	}

	removeDirectChildrenOfNodesInContainer(&changesWithNodes);
}

void DiffManager::visualize()
{
	auto diffSetup = initializeDiffPrerequisites();

	FilePersistence::Diff diff = diffSetup.repository_->diff(oldVersion_, newVersion_);

	auto changes = diff.changes();

	// detailed changes
	QList<ChangeWithNodes> changesWithNodes;

	// contains the nodes which will be drawn
	QSet<Model::NodeIdType> changedNodesToVisualize;

	// fill up lists
	computeChangeNodesAndNodesToVisualize(changes, changesWithNodes, changedNodesToVisualize, diffSetup);

	Visualization::VisualizationManager::instance().mainScene()->listenToTreeManager(diffSetup.newVersionManager_);
	Visualization::VisualizationManager::instance().mainScene()->listenToTreeManager(diffSetup.oldVersionManager_);

	auto diffViewItem = Visualization::VisualizationManager::instance().mainScene()->
			viewItems()->viewItem("DiffView");

	if (diffViewItem)
		Visualization::VisualizationManager::instance().mainScene()->
					viewItems()->removeViewItem(diffViewItem);

	diffViewItem = Visualization::VisualizationManager::instance().mainScene()->
				viewItems()->newViewItem("DiffView");

	diffViewItem->setMajorAxis(Visualization::GridLayouter::NoMajor);
	diffViewItem->setZoomLabelsEnabled(false);

	for (auto diffComparisonPair : createDiffComparisonPairs(diffSetup, changedNodesToVisualize))
		diffViewItem->insertNode(diffComparisonPair);

	// create visualization for changes
	Visualization::VisualizationManager::instance().mainScene()->addPostEventAction(
								  [diffViewItem, changesWithNodes]() {
		createOverlaysForChanges(diffViewItem, changesWithNodes);
	});

	// switch to the newly created view
	Visualization::VisualizationManager::instance().mainScene()->viewItems()->switchToView(diffViewItem);
}

// TODO maybe better implementation for this
void DiffManager::removeDirectChildrenOfNodesInContainer(QList<ChangeWithNodes>* container)
{
	QHash<Model::Node*, FilePersistence::ChangeType> oldNodes;
	QHash<Model::Node*, FilePersistence::ChangeType> newNodes;

	for (auto change : *container)
	{
		if (change.oldNode_)
			oldNodes.insert(change.oldNode_, change.changeType_);
		if (change.newNode_)
			newNodes.insert(change.newNode_, change.changeType_);
	}

	QSet<Model::Node*> oldNodesMarkedForRemoval = findAllNodesWithDirectParentPresent(oldNodes);
	QSet<Model::Node*> newNodesMarkedForRemoval = findAllNodesWithDirectParentPresent(newNodes);

	auto it = container->begin();
	while (it != container->end())
	{
		if (oldNodesMarkedForRemoval.contains(it->oldNode_) || newNodesMarkedForRemoval.contains(it->newNode_))
			it = container->erase(it);
		else
			it++;
	}
}

QSet<Model::Node*> DiffManager::findAllNodesWithDirectParentPresent(QHash<Model::Node*,
																						  FilePersistence::ChangeType>& nodes)
{
	QSet<Model::Node*> result;
	for (auto node : nodes.keys())
		// check that changeType also matches
		if (nodes.contains(node->parent()) && nodes[node] == nodes[node->parent()])
				result.insert(node);

	return result;
}

QSet<Visualization::Item*> DiffManager::findAllItemsWithAncestorsIn(QSet<Visualization::Item*> items)
{
	QSet<Visualization::Item*> result;
	for (auto item : items)
	{
		auto parent = item->parent();
		while (parent)
		{
			if (items.contains(parent))
			{
				result.insert(item);
				break;
			}
			parent = parent->parent();
		}
	}

	return result;
}

// TODO find good way to return and use Node instead of Id
bool DiffManager::findChangedNode(Model::TreeManager* treeManager, Model::NodeIdType id, Model::NodeIdType& resultId)
{
	if (auto node = const_cast<Model::Node*>(treeManager->nodeIdMap().node(id)))
	{

		Model::Node* changedNode = nullptr;

		if (auto ancestorNode = node->firstAncestorOfType(contextUnitMatcher_))
		{
			changedNode = ancestorNode;

			resultId = treeManager->nodeIdMap().id(changedNode);
			return true;
		}
		else
		{
			resultId = treeManager->nodeIdMap().id(node);
		}
		return true;
	}
	return false;
}

void DiffManager::createOverlaysForChanges(Visualization::ViewItem* diffViewItem,
														 QList<ChangeWithNodes> changesWithNodes)
{
	static const QString arrowLayer = "move_arrows";
	diffViewItem->setArrowStyle(arrowLayer, "thick");

	QSet<Visualization::Item*> allItemsToScale;
	for (auto change : changesWithNodes)
	{
		QString highlightOverlayStyle;
		QString highlightOverlayName;

		switch (change.changeType_)
		{
			case FilePersistence::ChangeType::Deletion:
				highlightOverlayName = "delete_highlights";
				highlightOverlayStyle = "delete_no_bg_solid_outline";
				break;
			case FilePersistence::ChangeType::Insertion:
				highlightOverlayName = "insert_highlights";
				highlightOverlayStyle = "insert_no_bg_solid_outline";
				break;
			case FilePersistence::ChangeType::Move:
				highlightOverlayName = "move_highlights";
				highlightOverlayStyle = "move_no_bg_solid_outline";
				break;
			case FilePersistence::ChangeType::Stationary:
				highlightOverlayName = "modify_highlights";
				highlightOverlayStyle = "modify_no_bg_solid_outline";
				break;
			case FilePersistence::ChangeType::Unclassified:
				Q_ASSERT(false);
				break;
		}

		Visualization::Item* oldNodeItem = addHighlightAndReturnItem(change.oldNode_, diffViewItem,
																						highlightOverlayName, highlightOverlayStyle);;
		Visualization::Item* newNodeItem = addHighlightAndReturnItem(change.newNode_, diffViewItem,
																						highlightOverlayName, highlightOverlayStyle);;

		if (oldNodeItem)
			allItemsToScale.insert(oldNodeItem);

		if (newNodeItem)
			allItemsToScale.insert(newNodeItem);

		if (change.changeType_== FilePersistence::ChangeType::Move)
		{
			auto overlay = new Visualization::ArrowOverlay{oldNodeItem, newNodeItem,
					  Visualization::ArrowOverlay::itemStyles().get("thick")};
			diffViewItem->addOverlay(overlay, arrowLayer);
		}

	}

	QSet<Visualization::Item*> removeItems = findAllItemsWithAncestorsIn(allItemsToScale);

	allItemsToScale.subtract(removeItems);

	Visualization::VisualizationManager::instance().mainScene()->
			addOnZoomHandler([allItemsToScale](qreal factor)
	{
		for (auto item : allItemsToScale)
		{
			if (factor >= 1.0)
				item->setScale(1.0);
			else if (factor >= 0.05)
				item->setScale((1/factor));
			else
				item->setScale((1/factor) * std::pow(0.95, 1/factor));
		}
	});

	// set zoom level further out and center the scene
	Visualization::VisualizationManager::instance().mainView()->zoom(7);
	Visualization::VisualizationManager::instance().mainView()->
			centerOn(Visualization::VisualizationManager::instance().mainView()->sceneRect().center());
}

Visualization::Item* DiffManager::addHighlightAndReturnItem(Model::Node* node, Visualization::ViewItem* viewItem,
                                               QString highlightOverlayName, QString highlightOverlayStyle)
{
	Visualization::Item* resultItem = nullptr;
	if (node)
	{
		if ((resultItem = viewItem->findVisualizationOf(node)))
		{
			auto overlay = new Visualization::HighlightOverlay{resultItem,
					Visualization::HighlightOverlay::itemStyles().get(highlightOverlayStyle)};
			resultItem->addOverlay(overlay, highlightOverlayName);
		}
		else if (auto parent = DCast<Model::CompositeNode>(node->parent()))
		{
			auto index = parent->indexOf(node);
			if (!parent->meta().attribute(index).name().startsWith("_"))
				return addHighlightAndReturnItem(node->parent(), viewItem, highlightOverlayName, highlightOverlayStyle);
		}
	}
	return resultItem;
}

QList<DiffComparisonPair*> DiffManager::createDiffComparisonPairs(DiffSetup& diffSetup,
													 QSet<Model::NodeIdType> diffComparisonPairNodeIds)
{
	QList<DiffComparisonPair*> diffComparisonPairs;
	for (auto id : diffComparisonPairNodeIds)
	{
		auto oldNode = const_cast<Model::Node*>(diffSetup.oldVersionManager_->nodeIdMap().node(id));
		auto newNode = const_cast<Model::Node*>(diffSetup.newVersionManager_->nodeIdMap().node(id));

		auto diffNode = new DiffComparisonPair{oldNode, newNode};

		diffComparisonPairs.append(diffNode);
	}
	return diffComparisonPairs;
}

Model::TreeManager* DiffManager::createTreeManagerFromVersion(FilePersistence::GitRepository* repository,
																				  QString version)
{
	if (version == FilePersistence::GitRepository::WORKDIR)
	{
		auto fileStore = new FilePersistence::SimpleTextFileStore {"projects/"};
		Model::TreeManager* versionTreeManager = new Model::TreeManager{};
		versionTreeManager->load(fileStore, project_, false);

		return versionTreeManager;

	}

	std::unique_ptr<const FilePersistence::Commit> commit{repository->getCommit(version)};

	auto fileStore = new FilePersistence::SimpleTextFileStore {
				[this, &commit](QString filename, const char*& data, int& size)
				{ return commit->getFileContent(filename, data, size, false); }
			};

	Model::TreeManager* versionTreeManager = new Model::TreeManager{};
	versionTreeManager->load(fileStore, project_, false);

	return versionTreeManager;
}

}
<|endoftext|>


<commit_before>#include <iostream>
#include <cassert>
#include "days.hpp"
#include "utils.hpp"

namespace {
    bool bounds_check(aoc2019::IntCodeComputer computer, std::int64_t x, std::int64_t y) {
        std::deque<std::int64_t> output_buffer;
        computer.connectOutput(output_buffer);

        computer.sendInput(x);
        computer.sendInput(y);

        computer.run();
        assert(computer.isTerminated());
        assert(!output_buffer.empty());

        return output_buffer.front();
    }
}

void aoc2019::day19_part1(std::istream &input, std::ostream &output) {
    IntCodeComputer computer(input);

    std::int64_t covered = 0;
    int last_width = 1;
    int last_start = 0;
    for (std::int64_t y = 0; y < 50; ++y) {
        auto x = last_start;

        while (!bounds_check(computer, x, y) && x < 50) {
            ++x;
        }

        if (x == 50) break;

        last_start = x;
        x += last_width - 1;

        if (!bounds_check(computer, x, y)) {
            std::cerr << x << "," << y << "," << covered << std::endl;
            throw std::logic_error("Assumption false");
        }

        while (bounds_check(computer, x, y) && x < 50) {
            ++x;
        }

        last_width = x - last_start;
        covered += last_width;
    }

    output << covered << std::endl;
}

void aoc2019::day19_part2(std::istream &input, std::ostream &output) {
	output << "Not implemented\n";
}
<commit_msg>Implement day 19 part 2.<commit_after>#include <iostream>
#include <cassert>
#include <queue>
#include "days.hpp"
#include "utils.hpp"

namespace {
    bool bounds_check(aoc2019::IntCodeComputer computer, std::int64_t x, std::int64_t y) {
        std::deque<std::int64_t> output_buffer;
        computer.connectOutput(output_buffer);

        computer.sendInput(x);
        computer.sendInput(y);

        computer.run();
        assert(computer.isTerminated());
        assert(!output_buffer.empty());

        return output_buffer.front();
    }

    class Beam {
    private:
        aoc2019::IntCodeComputer computer;
        std::int64_t last_width = 1;
        std::int64_t last_start = 0;
        std::int64_t y = 0;

    public:
        Beam(std::istream &input) : computer(input) {};

        std::pair<std::int64_t, std::int64_t> next() {
            auto x = last_start;

            while (!bounds_check(computer, x, y)) {
                ++x;
            }

            last_start = x;
            x += last_width - 1;

            while (bounds_check(computer, x, y)) {
                ++x;
            }

            last_width = x - last_start;
            ++y;

            return {last_start, last_width};
        }
    };
}

void aoc2019::day19_part1(std::istream &input, std::ostream &output) {
    Beam beam(input);

    std::int64_t covered = 0;
    for (std::int64_t y = 0; y < 50; ++y) {
        const auto[start, width] = beam.next();

        if (start >= 50) break;

        covered += std::min(50 - start, width);
    }

    output << covered << std::endl;
}

void aoc2019::day19_part2(std::istream &input, std::ostream &output) {
    Beam beam(input);
    std::queue<std::int64_t> beam_ends;

    constexpr std::int64_t DIMENSION = 100;

    for (std::int64_t y = 0; true; ++y) {
        const auto[start, width] = beam.next();

        beam_ends.push(start + width);
        if (beam_ends.size() == DIMENSION) {
            auto end = beam_ends.front();
            if (end - start >= DIMENSION) {
                auto result = start * 10000 + y - DIMENSION + 1;
                output << result << std::endl;
                return;
            }
            beam_ends.pop();
        }
    }
}
<|endoftext|>