Se analiza el consumo eléctrico de una casa con los datos tomados a lo largo de 4 años. En la casa hay instalados 3 submedidores, para distintas zonas de estas. Analizamos las medidas tomadas por éstos el y el consumo global activo.
Cargamos librerías usadas y los datos a analizar. Juntamos fecha y hora en una columna nueva
library(dplyr)
library(tidyr)
library(ggplot2)
library(lubridate)
#detach(package:plyr)
setwd("C://Users/Pau A/Documents/Data Analysis Course/3- Deep Analytics and Visualization/Task 1")
The working directory was changed to C:/Users/Pau A/Documents/Data Analysis Course/3- Deep Analytics and Visualization/Task 1 inside a notebook chunk. The working directory will be reset when the chunk is finished running. Use the knitr root.dir option in the setup chunk to change the the working directory for notebook chunks.
Consumption <- read.csv("household_power_consumption.txt", TRUE, ";", na.strings = c("NA","?"))
Preprocesamos los datos. Convertimos a númerico las columnas referentes al consumo global activo, reactivo, al voltaje y a los submedidores. Creamos nuevas columnas con los días de la semana y el mes. Y finalmente creamos un dataset sin NAs.
### Pre-proceso de los datos.
#Creamos una nueva columna con Fecha y Hora
Consumption <-cbind(Consumption,paste(Consumption$Date,Consumption$Time), stringsAsFactors=FALSE)
colnames(Consumption)[10] <-"DateTime"
Consumption <- Consumption[,c(ncol(Consumption), 1:(ncol(Consumption)-1))]
#Añadimos una columna con el GAP a W·h
Consumption <- cbind(Consumption, Consumption$Global_active_power*1000/60)
colnames(Consumption)[11] <- "Global_Consumption"
#Pasamos a númerico
Consumption$Global_active_power <- as.numeric(Consumption$Global_active_power)
Consumption$Global_reactive_power <- as.numeric(Consumption$Global_reactive_power)
Consumption$Voltage <- as.numeric(Consumption$Voltage)
Consumption$Sub_metering_1 <- as.numeric(Consumption$Sub_metering_1)
Consumption$Sub_metering_2 <- as.numeric(Consumption$Sub_metering_2)
Consumption$Sub_metering_3 <- as.numeric(Consumption$Sub_metering_3)
Consumption$Global_Consumption <- as.numeric(Consumption$Global_Consumption)
#Creamos una nueva columna con los d??as de la semana
Consumption <- cbind(Consumption,weekdays(as.Date(Consumption$Date, '%d/%m/%Y')), stringsAsFactors=FALSE)
colnames(Consumption)[12] <- "Day"
#Creamos una nueva columna con el mes
Consumption <- cbind(Consumption,months(as.Date(Consumption$Date, '%d/%m/%Y')), stringsAsFactors=FALSE)
colnames(Consumption)[13] <- "Month"
#Creamos una columna de mes/año
MonthYear <- separate(Consumption, Date, into=c("day", "month", "year"))
MonthYear <- paste(MonthYear$month, MonthYear$year, sep="/")
Consumption <-cbind(Consumption, MonthYear)
#Filtramos el dataset en datos de cada 15 minutos
Minute <- separate(Consumption, Time, into = c("Hour", "Minute", "Second"))
Minute <- Minute$Minute
Consumption <-cbind(Consumption, Minute)
Consumption$Minute <- as.numeric(Consumption$Minute)
Reduced_data <- subset(Consumption, Minute==1 | Minute==31)
#Añadimos columna de semanas
semana <- c(1:205)
initial <- c()
for (i in semana){
initial <- c(initial, rep(semana[[i]], 10080))
}
Consumption$Week <- c(initial, rep(206, 8859))
#Dataset sin missing values
cleanConsumption <- na.omit(Consumption)
Analizamos el consumo en los meses de Enero y Agosto, comparando de Lunes a Viernes y los fines de semana, para los submedidores y el consumo global activo.
###Enero
january <- subset(Reduced_data, Month == "enero")
january_weekend <- subset(Reduced_data, Month == "enero" & (Day=="s?bado" | Day=="domingo"))
january_weekday <- subset(Reduced_data, Month == "enero" & Day!="s?bado" & Day!="domingo")
#Sub1
j_sub_1_wd <- ggplot(january_weekday, aes(x=Time)) + geom_point(aes(y=Sub_metering_1), color = "red")
j_sub_1_wd + theme(axis.text.x = element_text(size=6, angle=90)) + xlab("Hora") + ylab("Sub-Metering 1") + ggtitle("Sub-Metering 1 en Enero, Lunes-Viernes")

j_sub_1_we <- ggplot(january_weekend, aes(x=Time)) + geom_point(aes(y=Sub_metering_1), color = "blue")
j_sub_1_we + theme(axis.text.x = element_text(size=6, angle=90)) + xlab("Hora") + ylab("Sub-Metering 1") + ggtitle("Sub-Metering 1 en Enero, fin de semana")

#Sub2
j_sub_2_wd <- ggplot(january_weekday, aes(x=Time)) + geom_point(aes(y=Sub_metering_2), color = "red")
j_sub_2_wd + theme(axis.text.x = element_text(size=6, angle=90)) + xlab("Hora") + ylab("Sub-Metering 2") + ggtitle("Sub-Metering 2 en Enero, Lunes-Viernes")

j_sub_2_we <- ggplot(january_weekend, aes(x=Time)) + geom_point(aes(y=Sub_metering_2), color = "blue")
j_sub_2_we + theme(axis.text.x = element_text(size=6, angle=90)) + xlab("Hora") + ylab("Sub-Metering 2") + ggtitle("Sub-Metering 2 en Enero, fin de semana")

#Sub3
j_sub_3_wd <- ggplot(january_weekday, aes(x=Time)) + geom_point(aes(y=Sub_metering_3), color = "red")
j_sub_3_wd + theme(axis.text.x = element_text(size=6, angle=90)) + xlab("Hora") + ylab("Sub-Metering 3") + ggtitle("Sub-Metering 3 en Enero, Lunes-Viernes")

j_sub_3_we <- ggplot(january_weekend, aes(x=Time)) + geom_point(aes(y=Sub_metering_2), color = "blue")
j_sub_3_we + theme(axis.text.x = element_text(size=6, angle=90)) + xlab("Hora") + ylab("Sub-Metering 3") + ggtitle("Sub-Metering 3 en Enero, fin de semana")

#GAP
j_gap_wd <- ggplot(january_weekday, aes(x=Time)) + geom_point(aes(y=Global_Consumption),color = "red")
j_gap_wd + theme(axis.text.x = element_text(size=6, angle=90)) + xlab("Hora") + ylab("Consumo Global Activo") + ggtitle("Consumo Global Activo en Enero, Lunes-Viernes")

j_gap_we <- ggplot(january_weekend, aes(x=Time)) + geom_point(aes(y=Sub_metering_2), color = "blue")
j_gap_we + theme(axis.text.x = element_text(size=6, angle=90)) + xlab("Hora") + ylab("Consumo Global Activo") + ggtitle("Consumo Global Activo en Enero, fin de semana")

###Agosto
august <- subset(Reduced_data, Month == "agosto")
august_weekend <- subset(Reduced_data, Month == "agosto" & (Day=="s?bado" | Day=="domingo"))
august_weekday <- subset(Reduced_data, Month == "agosto" & Day!="s?bado" & Day!="domingo")
#Sub1
a_sub_1_wd <- ggplot(august_weekday, aes(x=Time)) + geom_point(aes(y=Sub_metering_1), color = "red")
a_sub_1_wd + theme(axis.text.x = element_text(size=6, angle=90)) + xlab("Hora") + ylab("Sub-Metering 1") + ggtitle("Sub-Metering 1 en Agosto, Lunes-Viernes")

a_sub_1_we <- ggplot(august_weekend, aes(x=Time)) + geom_point(aes(y=Sub_metering_1), color = "blue")
a_sub_1_we + theme(axis.text.x = element_text(size=6, angle=90)) + xlab("Hora") + ylab("Sub-Metering 1") + ggtitle("Sub-Metering 1 en Agosto, fin de semana")

#Sub2
a_sub_2_wd <- ggplot(august_weekday, aes(x=Time)) + geom_point(aes(y=Sub_metering_2), color = "red")
a_sub_2_wd + theme(axis.text.x = element_text(size=6, angle=90)) + xlab("Hora") + ylab("Sub-Metering 2") + ggtitle("Sub-Metering 2 en Agosto, Lunes-Viernes")

a_sub_2_we <- ggplot(august_weekend, aes(x=Time)) + geom_point(aes(y=Sub_metering_2), color = "blue")
a_sub_2_we + theme(axis.text.x = element_text(size=6, angle=90)) + xlab("Hora") + ylab("Sub-Metering 2") + ggtitle("Sub-Metering 2 en Agosto, fin de semana")

#Sub3
a_sub_3_wd <- ggplot(august_weekday, aes(x=Time)) + geom_point(aes(y=Sub_metering_3), color = "red")
a_sub_3_wd + theme(axis.text.x = element_text(size=6, angle=90)) + xlab("Hora") + ylab("Sub-Metering 3") + ggtitle("Sub-Metering 3 en Agosto, Lunes-Viernes")

a_sub_3_we <- ggplot(august_weekend, aes(x=Time)) + geom_point(aes(y=Sub_metering_2), color = "blue")
a_sub_3_we + theme(axis.text.x = element_text(size=6, angle=90)) + xlab("Hora") + ylab("Sub-Metering 3") + ggtitle("Sub-Metering 3 en Agosto, fin de semana")

#GAP
a_gap_wd <- ggplot(august_weekday, aes(x=Time)) + geom_point(aes(y=Global_Consumption),color = "red")
a_gap_wd + theme(axis.text.x = element_text(size=6, angle=90)) + xlab("Hora") + ylab("Consumo Global Activo") + ggtitle("Consumo Global Activo en Agosto, Lunes-Viernes")

a_gap_we <- ggplot(august_weekend, aes(x=Time)) + geom_point(aes(y=Sub_metering_2), color = "blue")
a_gap_we + theme(axis.text.x = element_text(size=6, angle=90)) + xlab("Hora") + ylab("Consumo Global Activo") + ggtitle("Consumo Global Activo en Agosto, fin de semana")

Realizamos un análisis de las medias en cada hora para los meses de Enero y Agosto.
###Calculamos las medias por cada hora en cada mes
#Enero
january_mean <- january %>%
group_by(Time) %>%
summarise(
Sub_1_mean_january <- mean(Sub_metering_1, na.rm = TRUE),
Sub_2_mean_january <- mean(Sub_metering_2, na.rm = TRUE),
Sub_3_mean_january <- mean(Sub_metering_3, na.rm = TRUE),
GAP_mean_january <- mean(Global_Consumption, na.rm = TRUE)
)
colnames(january_mean) <- c("Time","Sub_1_mean","Sub_2_mean","Sub_3_mean","GAP_mean")
j_mean <- ggplot(january_mean, aes(x=Time, group=1)) + geom_line(aes(y=Sub_1_mean, color="Sub-Metering 1")) +
geom_line(aes(y=Sub_2_mean, color="Sub-Metering 2")) +
geom_line(aes(y=Sub_3_mean, color="Sub-Metering 3")) +
geom_line(aes(y=GAP_mean, color="Global Active Power")) +
scale_color_manual("", breaks=c("Sub-Metering 1","Sub-Metering 2","Sub-Metering 3","Global Active Power"),
values=c("red","blue","brown","green"))
j_mean + theme(axis.text.x = element_text(size=6, angle=90)) + ylab("W?h") +
labs(title="Consumo medio en un día de Enero") + xlab("Hora")

#Agosto
august_mean <- august %>%
group_by(Time) %>%
summarise(
Sub_1_mean_august <- mean(Sub_metering_1, na.rm = TRUE),
Sub_2_mean_august <- mean(Sub_metering_2, na.rm = TRUE),
Sub_3_mean_august <- mean(Sub_metering_3, na.rm = TRUE),
GAP_mean_august <- mean(Global_Consumption, na.rm = TRUE)
)
colnames(august_mean) <- c("Time","Sub_1_mean","Sub_2_mean","Sub_3_mean","GAP_mean")
a_mean <- ggplot(august_mean, aes(x=Time, group=1)) + geom_line(aes(y=Sub_1_mean, color="Sub-Metering 1")) +
geom_line(aes(y=Sub_2_mean, color="Sub-Metering 2")) +
geom_line(aes(y=Sub_3_mean, color="Sub-Metering 3")) +
geom_line(aes(y=GAP_mean, color="Global Active Power")) +
scale_color_manual("", breaks=c("Sub-Metering 1","Sub-Metering 2","Sub-Metering 3","Global Active Power"),
values=c("red","blue","brown","green"))
a_mean + theme(axis.text.x = element_text(size=6, angle=90)) + ylab("W?h") +
labs(title="Consumo medio en un día de Agosto") + xlab("Hora")

Posterioremente realizamos una visualización del consumo a lo largo del año 2009:
Year <- separate(cleanConsumption, Date, into = c("day", "month", "Year"))
Date_2 <- as.Date(paste(Year$day, Year$month, Year$Year), "%d %m %Y", tz="GMT")
Year <- Year$Year
cleanConsumption <-cbind(cleanConsumption, Year, Date_2)
cleanConsumption$Year <- as.numeric(cleanConsumption$Year)
data_2009 <- subset(cleanConsumption, Year==4)
year <- data_2009 %>%
group_by(Date_2) %>%
summarise(
Sub_1_mean <- mean(Sub_metering_1, na.rm = TRUE),
Sub_2_mean <- mean(Sub_metering_2, na.rm = TRUE),
Sub_3_mean <- mean(Sub_metering_3, na.rm = TRUE),
GAP_mean <- mean(Global_Consumption, na.rm = TRUE)
)
colnames(year) <- c("Date","Sub_1_mean","Sub_2_mean","Sub_3_mean","GAP_mean")
consumption_2009 <- ggplot(year, aes(x=Date, group=1)) + geom_line(aes(y=Sub_1_mean, color="Sub-Metering 1")) +
geom_line(aes(y=Sub_2_mean, color="Sub-Metering 2")) +
geom_line(aes(y=Sub_3_mean, color="Sub-Metering 3")) +
geom_line(aes(y=GAP_mean, color="Global Active Power")) +
scale_color_manual("", breaks=c("Sub-Metering 1","Sub-Metering 2","Sub-Metering 3","Global Active Power"),
values=c("red","blue","brown","green"))
consumption_2009 + theme(axis.text.x = element_text(size= 10, angle=45)) + ylab("W·h") +
labs(title="Consumo en 2009")

Y finalmente reealizamos un análisis del consumo a lo largo de todos los años. Podemos observar un comportamiento similar al sinusoidal con subidas de consumo en invierno y bajadas en verano.
#Evolución del consumo a lo largo de todos los años
Year <- separate(cleanConsumption, Date, into = c("day", "month", "Year"))
Date_2 <- as.Date(paste(Year$day, Year$month, Year$Year), "%d %m %Y", tz="GMT")
day <- Year$day
Year <- Year$Year
cleanConsumption <-cbind(cleanConsumption, Year, Date_2, day)
lesspoints <- subset(cleanConsumption, day==1 | day==5 | day==10 | day==15 | day==20 | day==25 | day==30)
evolution <- lesspoints %>%
group_by(Date_2) %>%
summarise(
Sub_1_mean <- mean(Sub_metering_1, na.rm = TRUE),
Sub_2_mean <- mean(Sub_metering_2, na.rm = TRUE),
Sub_3_mean <- mean(Sub_metering_3, na.rm = TRUE),
GAP_mean <- mean(Global_Consumption, na.rm = TRUE)
)
colnames(evolution) <- c("Date","Sub_1_mean","Sub_2_mean","Sub_3_mean","GAP_mean")
cons_evol <- ggplot(evolution, aes(x=Date, group=1)) + geom_line(aes(y=Sub_1_mean, color="Sub-Metering 1")) +
geom_line(aes(y=Sub_2_mean, color="Sub-Metering 2")) +
geom_line(aes(y=Sub_3_mean, color="Sub-Metering 3")) +
geom_line(aes(y=GAP_mean, color="Global Active Power")) +
scale_color_manual("", breaks=c("Sub-Metering 1","Sub-Metering 2","Sub-Metering 3","Global Active Power"),
values=c("red","blue","brown","green"))
cons_evol + theme(axis.text.x = element_text(size= 10, angle=45)) + ylab("W?h") + xlab("Fecha") +
labs(title="Evolución del consumo")

LS0tDQp0aXRsZTogIkFuw6FsaXNpcyB5IHZpc3VhbGl6YWNpw7NuIGRlIGNvbnN1bW8gZWzDqWN0cmljbyINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNClNlIGFuYWxpemEgZWwgY29uc3VtbyBlbMOpY3RyaWNvIGRlIHVuYSBjYXNhIGNvbiBsb3MgZGF0b3MgdG9tYWRvcyBhIGxvIGxhcmdvIGRlIDQgYcOxb3MuIEVuIGxhIGNhc2EgaGF5IGluc3RhbGFkb3MgMyBzdWJtZWRpZG9yZXMsIHBhcmEgZGlzdGludGFzIHpvbmFzIGRlIGVzdGFzLiBBbmFsaXphbW9zIGxhcyBtZWRpZGFzIHRvbWFkYXMgcG9yIMOpc3RvcyBlbCB5IGVsIGNvbnN1bW8gZ2xvYmFsIGFjdGl2by4gDQoNCkNhcmdhbW9zIGxpYnJlcsOtYXMgdXNhZGFzIHkgbG9zIGRhdG9zIGEgYW5hbGl6YXIuIEp1bnRhbW9zIGZlY2hhIHkgaG9yYSBlbiB1bmEgY29sdW1uYSBudWV2YQ0KYGBge3J9DQoNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KHRpZHlyKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShsdWJyaWRhdGUpDQojZGV0YWNoKHBhY2thZ2U6cGx5cikNCg0Kc2V0d2QoIkM6Ly9Vc2Vycy9QYXUgQS9Eb2N1bWVudHMvRGF0YSBBbmFseXNpcyBDb3Vyc2UvMy0gRGVlcCBBbmFseXRpY3MgYW5kIFZpc3VhbGl6YXRpb24vVGFzayAxIikNCkNvbnN1bXB0aW9uIDwtIHJlYWQuY3N2KCJob3VzZWhvbGRfcG93ZXJfY29uc3VtcHRpb24udHh0IiwgVFJVRSwgIjsiLCBuYS5zdHJpbmdzID0gYygiTkEiLCI/IikpDQoNCmBgYA0KUHJlcHJvY2VzYW1vcyBsb3MgZGF0b3MuIENvbnZlcnRpbW9zIGEgbsO6bWVyaWNvIGxhcyBjb2x1bW5hcyByZWZlcmVudGVzIGFsIGNvbnN1bW8gZ2xvYmFsIGFjdGl2bywgcmVhY3Rpdm8sIGFsIHZvbHRhamUgeSBhIGxvcyBzdWJtZWRpZG9yZXMuIENyZWFtb3MgbnVldmFzIGNvbHVtbmFzIGNvbiBsb3MgZMOtYXMgZGUgbGEgc2VtYW5hIHkgZWwgbWVzLiBZIGZpbmFsbWVudGUgY3JlYW1vcyB1biBkYXRhc2V0IHNpbiBOQXMuDQpgYGB7cn0NCiMjIyBQcmUtcHJvY2VzbyBkZSBsb3MgZGF0b3MuIA0KI0NyZWFtb3MgdW5hIG51ZXZhIGNvbHVtbmEgY29uIEZlY2hhIHkgSG9yYQ0KQ29uc3VtcHRpb24gPC1jYmluZChDb25zdW1wdGlvbixwYXN0ZShDb25zdW1wdGlvbiREYXRlLENvbnN1bXB0aW9uJFRpbWUpLCBzdHJpbmdzQXNGYWN0b3JzPUZBTFNFKQ0KY29sbmFtZXMoQ29uc3VtcHRpb24pWzEwXSA8LSJEYXRlVGltZSINCkNvbnN1bXB0aW9uIDwtIENvbnN1bXB0aW9uWyxjKG5jb2woQ29uc3VtcHRpb24pLCAxOihuY29sKENvbnN1bXB0aW9uKS0xKSldDQoNCg0KI0HDsWFkaW1vcyB1bmEgY29sdW1uYSBjb24gZWwgR0FQIGEgV8K3aA0KQ29uc3VtcHRpb24gPC0gY2JpbmQoQ29uc3VtcHRpb24sIENvbnN1bXB0aW9uJEdsb2JhbF9hY3RpdmVfcG93ZXIqMTAwMC82MCkNCmNvbG5hbWVzKENvbnN1bXB0aW9uKVsxMV0gPC0gIkdsb2JhbF9Db25zdW1wdGlvbiINCg0KDQojUGFzYW1vcyBhIG7Dum1lcmljbw0KQ29uc3VtcHRpb24kR2xvYmFsX2FjdGl2ZV9wb3dlciA8LSBhcy5udW1lcmljKENvbnN1bXB0aW9uJEdsb2JhbF9hY3RpdmVfcG93ZXIpDQpDb25zdW1wdGlvbiRHbG9iYWxfcmVhY3RpdmVfcG93ZXIgPC0gYXMubnVtZXJpYyhDb25zdW1wdGlvbiRHbG9iYWxfcmVhY3RpdmVfcG93ZXIpDQpDb25zdW1wdGlvbiRWb2x0YWdlIDwtIGFzLm51bWVyaWMoQ29uc3VtcHRpb24kVm9sdGFnZSkNCkNvbnN1bXB0aW9uJFN1Yl9tZXRlcmluZ18xIDwtIGFzLm51bWVyaWMoQ29uc3VtcHRpb24kU3ViX21ldGVyaW5nXzEpDQpDb25zdW1wdGlvbiRTdWJfbWV0ZXJpbmdfMiA8LSBhcy5udW1lcmljKENvbnN1bXB0aW9uJFN1Yl9tZXRlcmluZ18yKQ0KQ29uc3VtcHRpb24kU3ViX21ldGVyaW5nXzMgPC0gYXMubnVtZXJpYyhDb25zdW1wdGlvbiRTdWJfbWV0ZXJpbmdfMykNCkNvbnN1bXB0aW9uJEdsb2JhbF9Db25zdW1wdGlvbiA8LSBhcy5udW1lcmljKENvbnN1bXB0aW9uJEdsb2JhbF9Db25zdW1wdGlvbikNCg0KI0NyZWFtb3MgdW5hIG51ZXZhIGNvbHVtbmEgY29uIGxvcyBkPz9hcyBkZSBsYSBzZW1hbmENCkNvbnN1bXB0aW9uIDwtIGNiaW5kKENvbnN1bXB0aW9uLHdlZWtkYXlzKGFzLkRhdGUoQ29uc3VtcHRpb24kRGF0ZSwgJyVkLyVtLyVZJykpLCBzdHJpbmdzQXNGYWN0b3JzPUZBTFNFKQ0KY29sbmFtZXMoQ29uc3VtcHRpb24pWzEyXSA8LSAiRGF5Ig0KDQojQ3JlYW1vcyB1bmEgbnVldmEgY29sdW1uYSBjb24gZWwgbWVzDQpDb25zdW1wdGlvbiA8LSBjYmluZChDb25zdW1wdGlvbixtb250aHMoYXMuRGF0ZShDb25zdW1wdGlvbiREYXRlLCAnJWQvJW0vJVknKSksIHN0cmluZ3NBc0ZhY3RvcnM9RkFMU0UpDQpjb2xuYW1lcyhDb25zdW1wdGlvbilbMTNdIDwtICJNb250aCINCg0KI0NyZWFtb3MgdW5hIGNvbHVtbmEgZGUgbWVzL2HDsW8NCk1vbnRoWWVhciA8LSBzZXBhcmF0ZShDb25zdW1wdGlvbiwgRGF0ZSwgaW50bz1jKCJkYXkiLCAibW9udGgiLCAieWVhciIpKQ0KTW9udGhZZWFyIDwtIHBhc3RlKE1vbnRoWWVhciRtb250aCwgTW9udGhZZWFyJHllYXIsIHNlcD0iLyIpDQpDb25zdW1wdGlvbiA8LWNiaW5kKENvbnN1bXB0aW9uLCBNb250aFllYXIpDQoNCiNGaWx0cmFtb3MgZWwgZGF0YXNldCBlbiBkYXRvcyBkZSBjYWRhIDE1IG1pbnV0b3MNCk1pbnV0ZSA8LSBzZXBhcmF0ZShDb25zdW1wdGlvbiwgVGltZSwgaW50byA9IGMoIkhvdXIiLCAiTWludXRlIiwgIlNlY29uZCIpKQ0KTWludXRlIDwtIE1pbnV0ZSRNaW51dGUNCkNvbnN1bXB0aW9uIDwtY2JpbmQoQ29uc3VtcHRpb24sIE1pbnV0ZSkNCkNvbnN1bXB0aW9uJE1pbnV0ZSA8LSBhcy5udW1lcmljKENvbnN1bXB0aW9uJE1pbnV0ZSkNClJlZHVjZWRfZGF0YSA8LSBzdWJzZXQoQ29uc3VtcHRpb24sIE1pbnV0ZT09MSB8IE1pbnV0ZT09MzEpDQoNCiNBw7FhZGltb3MgY29sdW1uYSBkZSBzZW1hbmFzIA0Kc2VtYW5hIDwtIGMoMToyMDUpIA0KaW5pdGlhbCA8LSBjKCkNCmZvciAoaSBpbiBzZW1hbmEpew0KICBpbml0aWFsIDwtIGMoaW5pdGlhbCwgcmVwKHNlbWFuYVtbaV1dLCAxMDA4MCkpDQp9DQoNCkNvbnN1bXB0aW9uJFdlZWsgPC0gYyhpbml0aWFsLCByZXAoMjA2LCA4ODU5KSkNCg0KI0RhdGFzZXQgc2luIG1pc3NpbmcgdmFsdWVzDQpjbGVhbkNvbnN1bXB0aW9uIDwtIG5hLm9taXQoQ29uc3VtcHRpb24pDQoNCmBgYA0KDQoNCkFuYWxpemFtb3MgZWwgY29uc3VtbyBlbiBsb3MgbWVzZXMgZGUgRW5lcm8geSBBZ29zdG8sIGNvbXBhcmFuZG8gZGUgTHVuZXMgYSBWaWVybmVzIHkgbG9zIGZpbmVzIGRlIHNlbWFuYSwgcGFyYSBsb3Mgc3VibWVkaWRvcmVzIHkgZWwgY29uc3VtbyBnbG9iYWwgYWN0aXZvLiANCg0KYGBge3J9DQojIyNFbmVybw0KamFudWFyeSA8LSBzdWJzZXQoUmVkdWNlZF9kYXRhLCBNb250aCA9PSAiZW5lcm8iKQ0KamFudWFyeV93ZWVrZW5kIDwtIHN1YnNldChSZWR1Y2VkX2RhdGEsIE1vbnRoID09ICJlbmVybyIgJiAoRGF5PT0icz9iYWRvIiB8IERheT09ImRvbWluZ28iKSkNCmphbnVhcnlfd2Vla2RheSA8LSBzdWJzZXQoUmVkdWNlZF9kYXRhLCBNb250aCA9PSAiZW5lcm8iICYgRGF5IT0icz9iYWRvIiAmIERheSE9ImRvbWluZ28iKQ0KDQojU3ViMQ0Kal9zdWJfMV93ZCA8LSBnZ3Bsb3QoamFudWFyeV93ZWVrZGF5LCBhZXMoeD1UaW1lKSkgKyBnZW9tX3BvaW50KGFlcyh5PVN1Yl9tZXRlcmluZ18xKSwgY29sb3IgPSAicmVkIikNCmpfc3ViXzFfd2QgKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplPTYsIGFuZ2xlPTkwKSkgKyB4bGFiKCJIb3JhIikgKyB5bGFiKCJTdWItTWV0ZXJpbmcgMSIpICsgZ2d0aXRsZSgiU3ViLU1ldGVyaW5nIDEgZW4gRW5lcm8sIEx1bmVzLVZpZXJuZXMiKQ0KDQpqX3N1Yl8xX3dlIDwtIGdncGxvdChqYW51YXJ5X3dlZWtlbmQsIGFlcyh4PVRpbWUpKSArIGdlb21fcG9pbnQoYWVzKHk9U3ViX21ldGVyaW5nXzEpLCBjb2xvciA9ICJibHVlIikNCmpfc3ViXzFfd2UgKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplPTYsIGFuZ2xlPTkwKSkgKyB4bGFiKCJIb3JhIikgKyB5bGFiKCJTdWItTWV0ZXJpbmcgMSIpICsgZ2d0aXRsZSgiU3ViLU1ldGVyaW5nIDEgZW4gRW5lcm8sIGZpbiBkZSBzZW1hbmEiKQ0KDQojU3ViMg0Kal9zdWJfMl93ZCA8LSBnZ3Bsb3QoamFudWFyeV93ZWVrZGF5LCBhZXMoeD1UaW1lKSkgKyBnZW9tX3BvaW50KGFlcyh5PVN1Yl9tZXRlcmluZ18yKSwgY29sb3IgPSAicmVkIikNCmpfc3ViXzJfd2QgKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplPTYsIGFuZ2xlPTkwKSkgKyB4bGFiKCJIb3JhIikgKyB5bGFiKCJTdWItTWV0ZXJpbmcgMiIpICsgZ2d0aXRsZSgiU3ViLU1ldGVyaW5nIDIgZW4gRW5lcm8sIEx1bmVzLVZpZXJuZXMiKQ0KDQpqX3N1Yl8yX3dlIDwtIGdncGxvdChqYW51YXJ5X3dlZWtlbmQsIGFlcyh4PVRpbWUpKSArIGdlb21fcG9pbnQoYWVzKHk9U3ViX21ldGVyaW5nXzIpLCBjb2xvciA9ICJibHVlIikNCmpfc3ViXzJfd2UgKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplPTYsIGFuZ2xlPTkwKSkgKyB4bGFiKCJIb3JhIikgKyB5bGFiKCJTdWItTWV0ZXJpbmcgMiIpICsgZ2d0aXRsZSgiU3ViLU1ldGVyaW5nIDIgZW4gRW5lcm8sIGZpbiBkZSBzZW1hbmEiKQ0KDQojU3ViMw0Kal9zdWJfM193ZCA8LSBnZ3Bsb3QoamFudWFyeV93ZWVrZGF5LCBhZXMoeD1UaW1lKSkgKyBnZW9tX3BvaW50KGFlcyh5PVN1Yl9tZXRlcmluZ18zKSwgY29sb3IgPSAicmVkIikNCmpfc3ViXzNfd2QgKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplPTYsIGFuZ2xlPTkwKSkgKyB4bGFiKCJIb3JhIikgKyB5bGFiKCJTdWItTWV0ZXJpbmcgMyIpICsgZ2d0aXRsZSgiU3ViLU1ldGVyaW5nIDMgZW4gRW5lcm8sIEx1bmVzLVZpZXJuZXMiKQ0KDQpqX3N1Yl8zX3dlIDwtIGdncGxvdChqYW51YXJ5X3dlZWtlbmQsIGFlcyh4PVRpbWUpKSArIGdlb21fcG9pbnQoYWVzKHk9U3ViX21ldGVyaW5nXzIpLCBjb2xvciA9ICJibHVlIikNCmpfc3ViXzNfd2UgKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplPTYsIGFuZ2xlPTkwKSkgKyB4bGFiKCJIb3JhIikgKyB5bGFiKCJTdWItTWV0ZXJpbmcgMyIpICsgZ2d0aXRsZSgiU3ViLU1ldGVyaW5nIDMgZW4gRW5lcm8sIGZpbiBkZSBzZW1hbmEiKQ0KDQojR0FQDQpqX2dhcF93ZCA8LSBnZ3Bsb3QoamFudWFyeV93ZWVrZGF5LCBhZXMoeD1UaW1lKSkgKyBnZW9tX3BvaW50KGFlcyh5PUdsb2JhbF9Db25zdW1wdGlvbiksY29sb3IgPSAicmVkIikNCmpfZ2FwX3dkICsgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZT02LCBhbmdsZT05MCkpICsgeGxhYigiSG9yYSIpICsgeWxhYigiQ29uc3VtbyBHbG9iYWwgQWN0aXZvIikgKyBnZ3RpdGxlKCJDb25zdW1vIEdsb2JhbCBBY3Rpdm8gZW4gRW5lcm8sIEx1bmVzLVZpZXJuZXMiKQ0KDQpqX2dhcF93ZSA8LSBnZ3Bsb3QoamFudWFyeV93ZWVrZW5kLCBhZXMoeD1UaW1lKSkgKyBnZW9tX3BvaW50KGFlcyh5PVN1Yl9tZXRlcmluZ18yKSwgY29sb3IgPSAiYmx1ZSIpDQpqX2dhcF93ZSArIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemU9NiwgYW5nbGU9OTApKSArIHhsYWIoIkhvcmEiKSArIHlsYWIoIkNvbnN1bW8gR2xvYmFsIEFjdGl2byIpICsgZ2d0aXRsZSgiQ29uc3VtbyBHbG9iYWwgQWN0aXZvIGVuIEVuZXJvLCBmaW4gZGUgc2VtYW5hIikNCg0KDQojIyNBZ29zdG8NCmF1Z3VzdCA8LSBzdWJzZXQoUmVkdWNlZF9kYXRhLCBNb250aCA9PSAiYWdvc3RvIikNCmF1Z3VzdF93ZWVrZW5kIDwtIHN1YnNldChSZWR1Y2VkX2RhdGEsIE1vbnRoID09ICJhZ29zdG8iICYgKERheT09InM/YmFkbyIgfCBEYXk9PSJkb21pbmdvIikpDQphdWd1c3Rfd2Vla2RheSA8LSBzdWJzZXQoUmVkdWNlZF9kYXRhLCBNb250aCA9PSAiYWdvc3RvIiAmIERheSE9InM/YmFkbyIgJiBEYXkhPSJkb21pbmdvIikNCg0KI1N1YjENCmFfc3ViXzFfd2QgPC0gZ2dwbG90KGF1Z3VzdF93ZWVrZGF5LCBhZXMoeD1UaW1lKSkgKyBnZW9tX3BvaW50KGFlcyh5PVN1Yl9tZXRlcmluZ18xKSwgY29sb3IgPSAicmVkIikNCmFfc3ViXzFfd2QgKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplPTYsIGFuZ2xlPTkwKSkgKyB4bGFiKCJIb3JhIikgKyB5bGFiKCJTdWItTWV0ZXJpbmcgMSIpICsgZ2d0aXRsZSgiU3ViLU1ldGVyaW5nIDEgZW4gQWdvc3RvLCBMdW5lcy1WaWVybmVzIikNCg0KYV9zdWJfMV93ZSA8LSBnZ3Bsb3QoYXVndXN0X3dlZWtlbmQsIGFlcyh4PVRpbWUpKSArIGdlb21fcG9pbnQoYWVzKHk9U3ViX21ldGVyaW5nXzEpLCBjb2xvciA9ICJibHVlIikNCmFfc3ViXzFfd2UgKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplPTYsIGFuZ2xlPTkwKSkgKyB4bGFiKCJIb3JhIikgKyB5bGFiKCJTdWItTWV0ZXJpbmcgMSIpICsgZ2d0aXRsZSgiU3ViLU1ldGVyaW5nIDEgZW4gQWdvc3RvLCBmaW4gZGUgc2VtYW5hIikNCg0KI1N1YjINCmFfc3ViXzJfd2QgPC0gZ2dwbG90KGF1Z3VzdF93ZWVrZGF5LCBhZXMoeD1UaW1lKSkgKyBnZW9tX3BvaW50KGFlcyh5PVN1Yl9tZXRlcmluZ18yKSwgY29sb3IgPSAicmVkIikNCmFfc3ViXzJfd2QgKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplPTYsIGFuZ2xlPTkwKSkgKyB4bGFiKCJIb3JhIikgKyB5bGFiKCJTdWItTWV0ZXJpbmcgMiIpICsgZ2d0aXRsZSgiU3ViLU1ldGVyaW5nIDIgZW4gQWdvc3RvLCBMdW5lcy1WaWVybmVzIikNCg0KYV9zdWJfMl93ZSA8LSBnZ3Bsb3QoYXVndXN0X3dlZWtlbmQsIGFlcyh4PVRpbWUpKSArIGdlb21fcG9pbnQoYWVzKHk9U3ViX21ldGVyaW5nXzIpLCBjb2xvciA9ICJibHVlIikNCmFfc3ViXzJfd2UgKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplPTYsIGFuZ2xlPTkwKSkgKyB4bGFiKCJIb3JhIikgKyB5bGFiKCJTdWItTWV0ZXJpbmcgMiIpICsgZ2d0aXRsZSgiU3ViLU1ldGVyaW5nIDIgZW4gQWdvc3RvLCBmaW4gZGUgc2VtYW5hIikNCg0KI1N1YjMNCmFfc3ViXzNfd2QgPC0gZ2dwbG90KGF1Z3VzdF93ZWVrZGF5LCBhZXMoeD1UaW1lKSkgKyBnZW9tX3BvaW50KGFlcyh5PVN1Yl9tZXRlcmluZ18zKSwgY29sb3IgPSAicmVkIikNCmFfc3ViXzNfd2QgKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplPTYsIGFuZ2xlPTkwKSkgKyB4bGFiKCJIb3JhIikgKyB5bGFiKCJTdWItTWV0ZXJpbmcgMyIpICsgZ2d0aXRsZSgiU3ViLU1ldGVyaW5nIDMgZW4gQWdvc3RvLCBMdW5lcy1WaWVybmVzIikNCg0KYV9zdWJfM193ZSA8LSBnZ3Bsb3QoYXVndXN0X3dlZWtlbmQsIGFlcyh4PVRpbWUpKSArIGdlb21fcG9pbnQoYWVzKHk9U3ViX21ldGVyaW5nXzIpLCBjb2xvciA9ICJibHVlIikNCmFfc3ViXzNfd2UgKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplPTYsIGFuZ2xlPTkwKSkgKyB4bGFiKCJIb3JhIikgKyB5bGFiKCJTdWItTWV0ZXJpbmcgMyIpICsgZ2d0aXRsZSgiU3ViLU1ldGVyaW5nIDMgZW4gQWdvc3RvLCBmaW4gZGUgc2VtYW5hIikNCg0KI0dBUA0KYV9nYXBfd2QgPC0gZ2dwbG90KGF1Z3VzdF93ZWVrZGF5LCBhZXMoeD1UaW1lKSkgKyBnZW9tX3BvaW50KGFlcyh5PUdsb2JhbF9Db25zdW1wdGlvbiksY29sb3IgPSAicmVkIikNCmFfZ2FwX3dkICsgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZT02LCBhbmdsZT05MCkpICsgeGxhYigiSG9yYSIpICsgeWxhYigiQ29uc3VtbyBHbG9iYWwgQWN0aXZvIikgKyBnZ3RpdGxlKCJDb25zdW1vIEdsb2JhbCBBY3Rpdm8gZW4gQWdvc3RvLCBMdW5lcy1WaWVybmVzIikNCg0KYV9nYXBfd2UgPC0gZ2dwbG90KGF1Z3VzdF93ZWVrZW5kLCBhZXMoeD1UaW1lKSkgKyBnZW9tX3BvaW50KGFlcyh5PVN1Yl9tZXRlcmluZ18yKSwgY29sb3IgPSAiYmx1ZSIpDQphX2dhcF93ZSArIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemU9NiwgYW5nbGU9OTApKSArIHhsYWIoIkhvcmEiKSArIHlsYWIoIkNvbnN1bW8gR2xvYmFsIEFjdGl2byIpICsgZ2d0aXRsZSgiQ29uc3VtbyBHbG9iYWwgQWN0aXZvIGVuIEFnb3N0bywgZmluIGRlIHNlbWFuYSIpDQoNCmBgYA0KDQpSZWFsaXphbW9zIHVuIGFuw6FsaXNpcyBkZSBsYXMgbWVkaWFzIGVuIGNhZGEgaG9yYSBwYXJhIGxvcyBtZXNlcyBkZSBFbmVybyB5IEFnb3N0by4NCg0KYGBge3J9DQojIyNDYWxjdWxhbW9zIGxhcyBtZWRpYXMgcG9yIGNhZGEgaG9yYSBlbiBjYWRhIG1lcw0KI0VuZXJvDQpqYW51YXJ5X21lYW4gPC0gamFudWFyeSAlPiUNCiAgZ3JvdXBfYnkoVGltZSkgJT4lDQogIHN1bW1hcmlzZSgNCiAgICBTdWJfMV9tZWFuX2phbnVhcnkgPC0gbWVhbihTdWJfbWV0ZXJpbmdfMSwgbmEucm0gPSBUUlVFKSwNCiAgICBTdWJfMl9tZWFuX2phbnVhcnkgPC0gbWVhbihTdWJfbWV0ZXJpbmdfMiwgbmEucm0gPSBUUlVFKSwNCiAgICBTdWJfM19tZWFuX2phbnVhcnkgPC0gbWVhbihTdWJfbWV0ZXJpbmdfMywgbmEucm0gPSBUUlVFKSwNCiAgICBHQVBfbWVhbl9qYW51YXJ5IDwtIG1lYW4oR2xvYmFsX0NvbnN1bXB0aW9uLCBuYS5ybSA9IFRSVUUpDQogICkNCg0KY29sbmFtZXMoamFudWFyeV9tZWFuKSA8LSBjKCJUaW1lIiwiU3ViXzFfbWVhbiIsIlN1Yl8yX21lYW4iLCJTdWJfM19tZWFuIiwiR0FQX21lYW4iKQ0KDQpqX21lYW4gPC0gZ2dwbG90KGphbnVhcnlfbWVhbiwgYWVzKHg9VGltZSwgZ3JvdXA9MSkpICsgZ2VvbV9saW5lKGFlcyh5PVN1Yl8xX21lYW4sIGNvbG9yPSJTdWItTWV0ZXJpbmcgMSIpKSArIA0KICBnZW9tX2xpbmUoYWVzKHk9U3ViXzJfbWVhbiwgY29sb3I9IlN1Yi1NZXRlcmluZyAyIikpICsgDQogIGdlb21fbGluZShhZXMoeT1TdWJfM19tZWFuLCBjb2xvcj0iU3ViLU1ldGVyaW5nIDMiKSkgKyANCiAgZ2VvbV9saW5lKGFlcyh5PUdBUF9tZWFuLCBjb2xvcj0iR2xvYmFsIEFjdGl2ZSBQb3dlciIpKSArDQogIHNjYWxlX2NvbG9yX21hbnVhbCgiIiwgYnJlYWtzPWMoIlN1Yi1NZXRlcmluZyAxIiwiU3ViLU1ldGVyaW5nIDIiLCJTdWItTWV0ZXJpbmcgMyIsIkdsb2JhbCBBY3RpdmUgUG93ZXIiKSwNCiAgICAgICAgICAgICAgICAgICAgIHZhbHVlcz1jKCJyZWQiLCJibHVlIiwiYnJvd24iLCJncmVlbiIpKQ0Kal9tZWFuICsgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZT02LCBhbmdsZT05MCkpICsgeWxhYigiVz9oIikgKw0KICBsYWJzKHRpdGxlPSJDb25zdW1vIG1lZGlvIGVuIHVuIGTDrWEgZGUgRW5lcm8iKSArIHhsYWIoIkhvcmEiKQ0KDQoNCiNBZ29zdG8NCmF1Z3VzdF9tZWFuIDwtIGF1Z3VzdCAlPiUNCiAgZ3JvdXBfYnkoVGltZSkgJT4lDQogIHN1bW1hcmlzZSgNCiAgICBTdWJfMV9tZWFuX2F1Z3VzdCA8LSBtZWFuKFN1Yl9tZXRlcmluZ18xLCBuYS5ybSA9IFRSVUUpLA0KICAgIFN1Yl8yX21lYW5fYXVndXN0IDwtIG1lYW4oU3ViX21ldGVyaW5nXzIsIG5hLnJtID0gVFJVRSksDQogICAgU3ViXzNfbWVhbl9hdWd1c3QgPC0gbWVhbihTdWJfbWV0ZXJpbmdfMywgbmEucm0gPSBUUlVFKSwNCiAgICBHQVBfbWVhbl9hdWd1c3QgPC0gbWVhbihHbG9iYWxfQ29uc3VtcHRpb24sIG5hLnJtID0gVFJVRSkNCiAgKQ0KDQpjb2xuYW1lcyhhdWd1c3RfbWVhbikgPC0gYygiVGltZSIsIlN1Yl8xX21lYW4iLCJTdWJfMl9tZWFuIiwiU3ViXzNfbWVhbiIsIkdBUF9tZWFuIikNCg0KYV9tZWFuIDwtIGdncGxvdChhdWd1c3RfbWVhbiwgYWVzKHg9VGltZSwgZ3JvdXA9MSkpICsgZ2VvbV9saW5lKGFlcyh5PVN1Yl8xX21lYW4sIGNvbG9yPSJTdWItTWV0ZXJpbmcgMSIpKSArIA0KICAgICAgICAgICAgIGdlb21fbGluZShhZXMoeT1TdWJfMl9tZWFuLCBjb2xvcj0iU3ViLU1ldGVyaW5nIDIiKSkgKyANCiAgICAgICAgICAgIGdlb21fbGluZShhZXMoeT1TdWJfM19tZWFuLCBjb2xvcj0iU3ViLU1ldGVyaW5nIDMiKSkgKyANCiAgICAgICAgICAgIGdlb21fbGluZShhZXMoeT1HQVBfbWVhbiwgY29sb3I9Ikdsb2JhbCBBY3RpdmUgUG93ZXIiKSkgKw0KICAgICAgICAgICAgc2NhbGVfY29sb3JfbWFudWFsKCIiLCBicmVha3M9YygiU3ViLU1ldGVyaW5nIDEiLCJTdWItTWV0ZXJpbmcgMiIsIlN1Yi1NZXRlcmluZyAzIiwiR2xvYmFsIEFjdGl2ZSBQb3dlciIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlcz1jKCJyZWQiLCJibHVlIiwiYnJvd24iLCJncmVlbiIpKQ0KYV9tZWFuICsgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZT02LCBhbmdsZT05MCkpICsgeWxhYigiVz9oIikgKw0KICAgICAgICAgICAgICAgIGxhYnModGl0bGU9IkNvbnN1bW8gbWVkaW8gZW4gdW4gZMOtYSBkZSBBZ29zdG8iKSArIHhsYWIoIkhvcmEiKQ0KYGBgDQoNCg0KUG9zdGVyaW9yZW1lbnRlIHJlYWxpemFtb3MgdW5hIHZpc3VhbGl6YWNpw7NuIGRlbCBjb25zdW1vIGEgbG8gbGFyZ28gZGVsIGHDsW8gMjAwOToNCg0KYGBge3J9DQpZZWFyIDwtIHNlcGFyYXRlKGNsZWFuQ29uc3VtcHRpb24sIERhdGUsIGludG8gPSBjKCJkYXkiLCAibW9udGgiLCAiWWVhciIpKQ0KRGF0ZV8yIDwtIGFzLkRhdGUocGFzdGUoWWVhciRkYXksIFllYXIkbW9udGgsIFllYXIkWWVhciksICIlZCAlbSAlWSIsIHR6PSJHTVQiKQ0KWWVhciA8LSBZZWFyJFllYXINCmNsZWFuQ29uc3VtcHRpb24gPC1jYmluZChjbGVhbkNvbnN1bXB0aW9uLCBZZWFyLCBEYXRlXzIpDQpjbGVhbkNvbnN1bXB0aW9uJFllYXIgPC0gYXMubnVtZXJpYyhjbGVhbkNvbnN1bXB0aW9uJFllYXIpDQpkYXRhXzIwMDkgPC0gc3Vic2V0KGNsZWFuQ29uc3VtcHRpb24sIFllYXI9PTQpDQoNCnllYXIgPC0gZGF0YV8yMDA5ICU+JQ0KICBncm91cF9ieShEYXRlXzIpICU+JQ0KICBzdW1tYXJpc2UoDQogICAgU3ViXzFfbWVhbiA8LSBtZWFuKFN1Yl9tZXRlcmluZ18xLCBuYS5ybSA9IFRSVUUpLA0KICAgIFN1Yl8yX21lYW4gPC0gbWVhbihTdWJfbWV0ZXJpbmdfMiwgbmEucm0gPSBUUlVFKSwNCiAgICBTdWJfM19tZWFuIDwtIG1lYW4oU3ViX21ldGVyaW5nXzMsIG5hLnJtID0gVFJVRSksDQogICAgR0FQX21lYW4gPC0gbWVhbihHbG9iYWxfQ29uc3VtcHRpb24sIG5hLnJtID0gVFJVRSkNCiAgKQ0KDQpjb2xuYW1lcyh5ZWFyKSA8LSBjKCJEYXRlIiwiU3ViXzFfbWVhbiIsIlN1Yl8yX21lYW4iLCJTdWJfM19tZWFuIiwiR0FQX21lYW4iKQ0KDQpjb25zdW1wdGlvbl8yMDA5IDwtIGdncGxvdCh5ZWFyLCBhZXMoeD1EYXRlLCBncm91cD0xKSkgKyBnZW9tX2xpbmUoYWVzKHk9U3ViXzFfbWVhbiwgY29sb3I9IlN1Yi1NZXRlcmluZyAxIikpICsgDQogIGdlb21fbGluZShhZXMoeT1TdWJfMl9tZWFuLCBjb2xvcj0iU3ViLU1ldGVyaW5nIDIiKSkgKyANCiAgZ2VvbV9saW5lKGFlcyh5PVN1Yl8zX21lYW4sIGNvbG9yPSJTdWItTWV0ZXJpbmcgMyIpKSArIA0KICBnZW9tX2xpbmUoYWVzKHk9R0FQX21lYW4sIGNvbG9yPSJHbG9iYWwgQWN0aXZlIFBvd2VyIikpICsNCiAgc2NhbGVfY29sb3JfbWFudWFsKCIiLCBicmVha3M9YygiU3ViLU1ldGVyaW5nIDEiLCJTdWItTWV0ZXJpbmcgMiIsIlN1Yi1NZXRlcmluZyAzIiwiR2xvYmFsIEFjdGl2ZSBQb3dlciIpLA0KICAgICAgICAgICAgICAgICAgICAgdmFsdWVzPWMoInJlZCIsImJsdWUiLCJicm93biIsImdyZWVuIikpDQpjb25zdW1wdGlvbl8yMDA5ICsgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZT0gMTAsIGFuZ2xlPTQ1KSkgKyB5bGFiKCJXwrdoIikgKw0KICBsYWJzKHRpdGxlPSJDb25zdW1vIGVuIDIwMDkiKQ0KYGBgDQoNClkgZmluYWxtZW50ZSByZWVhbGl6YW1vcyB1biBhbsOhbGlzaXMgZGVsIGNvbnN1bW8gYSBsbyBsYXJnbyBkZSB0b2RvcyBsb3MgYcOxb3MuIFBvZGVtb3Mgb2JzZXJ2YXIgdW4gY29tcG9ydGFtaWVudG8gc2ltaWxhciBhbCBzaW51c29pZGFsIGNvbiBzdWJpZGFzIGRlIGNvbnN1bW8gZW4gaW52aWVybm8geSBiYWphZGFzIGVuIHZlcmFuby4NCg0KYGBge3J9DQojRXZvbHVjacOzbiBkZWwgY29uc3VtbyBhIGxvIGxhcmdvIGRlIHRvZG9zIGxvcyBhw7Fvcw0KDQpZZWFyIDwtIHNlcGFyYXRlKGNsZWFuQ29uc3VtcHRpb24sIERhdGUsIGludG8gPSBjKCJkYXkiLCAibW9udGgiLCAiWWVhciIpKQ0KRGF0ZV8yIDwtIGFzLkRhdGUocGFzdGUoWWVhciRkYXksIFllYXIkbW9udGgsIFllYXIkWWVhciksICIlZCAlbSAlWSIsIHR6PSJHTVQiKQ0KZGF5IDwtIFllYXIkZGF5DQpZZWFyIDwtIFllYXIkWWVhcg0KY2xlYW5Db25zdW1wdGlvbiA8LWNiaW5kKGNsZWFuQ29uc3VtcHRpb24sIFllYXIsIERhdGVfMiwgZGF5KQ0KbGVzc3BvaW50cyA8LSBzdWJzZXQoY2xlYW5Db25zdW1wdGlvbiwgZGF5PT0xIHwgZGF5PT01IHwgZGF5PT0xMCB8IGRheT09MTUgfCBkYXk9PTIwIHwgZGF5PT0yNSB8IGRheT09MzApDQoNCmV2b2x1dGlvbiA8LSBsZXNzcG9pbnRzICU+JQ0KICBncm91cF9ieShEYXRlXzIpICU+JQ0KICBzdW1tYXJpc2UoDQogICAgU3ViXzFfbWVhbiA8LSBtZWFuKFN1Yl9tZXRlcmluZ18xLCBuYS5ybSA9IFRSVUUpLA0KICAgIFN1Yl8yX21lYW4gPC0gbWVhbihTdWJfbWV0ZXJpbmdfMiwgbmEucm0gPSBUUlVFKSwNCiAgICBTdWJfM19tZWFuIDwtIG1lYW4oU3ViX21ldGVyaW5nXzMsIG5hLnJtID0gVFJVRSksDQogICAgR0FQX21lYW4gPC0gbWVhbihHbG9iYWxfQ29uc3VtcHRpb24sIG5hLnJtID0gVFJVRSkNCikNCg0KY29sbmFtZXMoZXZvbHV0aW9uKSA8LSBjKCJEYXRlIiwiU3ViXzFfbWVhbiIsIlN1Yl8yX21lYW4iLCJTdWJfM19tZWFuIiwiR0FQX21lYW4iKQ0KDQoNCmNvbnNfZXZvbCA8LSBnZ3Bsb3QoZXZvbHV0aW9uLCBhZXMoeD1EYXRlLCBncm91cD0xKSkgKyBnZW9tX2xpbmUoYWVzKHk9U3ViXzFfbWVhbiwgY29sb3I9IlN1Yi1NZXRlcmluZyAxIikpICsgDQogIGdlb21fbGluZShhZXMoeT1TdWJfMl9tZWFuLCBjb2xvcj0iU3ViLU1ldGVyaW5nIDIiKSkgKyANCiAgZ2VvbV9saW5lKGFlcyh5PVN1Yl8zX21lYW4sIGNvbG9yPSJTdWItTWV0ZXJpbmcgMyIpKSArIA0KICBnZW9tX2xpbmUoYWVzKHk9R0FQX21lYW4sIGNvbG9yPSJHbG9iYWwgQWN0aXZlIFBvd2VyIikpICsNCiAgc2NhbGVfY29sb3JfbWFudWFsKCIiLCBicmVha3M9YygiU3ViLU1ldGVyaW5nIDEiLCJTdWItTWV0ZXJpbmcgMiIsIlN1Yi1NZXRlcmluZyAzIiwiR2xvYmFsIEFjdGl2ZSBQb3dlciIpLA0KICAgICAgICAgICAgICAgICAgICAgdmFsdWVzPWMoInJlZCIsImJsdWUiLCJicm93biIsImdyZWVuIikpDQpjb25zX2V2b2wgKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplPSAxMCwgYW5nbGU9NDUpKSArIHlsYWIoIlc/aCIpICsgeGxhYigiRmVjaGEiKSArDQogIGxhYnModGl0bGU9IkV2b2x1Y2nDs24gZGVsIGNvbnN1bW8iKQ0KDQpgYGANCg0K