Contexto
La base de datos avocado.csv contiene datos sobre la venta de
aguacates en Estados Unidos, lo cual incluye datos como precios,
regiones, volumen de ventas, entre otros. La información que se busca
obtener de esta base de datos es analizar el comportamiento de precios y
ventas, para posteriormente obtener un pronóstico de ventas.
Análisis
Lectura de la base de datos
av <- read.csv("C:\\Users\\art191127\\Downloads\\avocado.csv")
Carga de librerías
library(tidyverse)
## ── Attaching packages ─────────────────────────────────────── tidyverse 1.3.2 ──
## ✔ ggplot2 3.4.1 ✔ purrr 1.0.1
## ✔ tibble 3.1.8 ✔ dplyr 1.1.0
## ✔ tidyr 1.3.0 ✔ stringr 1.5.0
## ✔ readr 2.1.4 ✔ forcats 1.0.0
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag() masks stats::lag()
library(lubridate)
##
## Attaching package: 'lubridate'
##
## The following objects are masked from 'package:base':
##
## date, intersect, setdiff, union
library(scales)
##
## Attaching package: 'scales'
##
## The following object is masked from 'package:purrr':
##
## discard
##
## The following object is masked from 'package:readr':
##
## col_factor
options("install.lock"=FALSE)
library(ggmap)
## ℹ Google's Terms of Service: <]8;;https://mapsplatform.google.comhttps://mapsplatform.google.com]8;;>
## ℹ Please cite ggmap if you use it! Use `citation("ggmap")` for details.
library(dplyr)
avocados <- av
names(avocados)
## [1] "Date" "AveragePrice" "Total.Volume" "X4046" "X4225"
## [6] "X4770" "Total.Bags" "Small.Bags" "Large.Bags" "XLarge.Bags"
## [11] "type" "year" "region"
Resumen de la base de datos aún sin modificar.
names(av)[names(av) == "ï..Date"] <- "Date"
names(avocados)[names(avocados) == "ï..Date"] <- "Date"
avocados$Date <- as.Date(avocados$Date, "%d/%m/%Y")
avocados$region <- as.factor(avocados$region)
avocados$type <- as.factor(avocados$type)
summary(avocados)
## Date AveragePrice Total.Volume X4046
## Min. :2015-01-04 Min. :0.440 Min. : 85 Min. : 0
## 1st Qu.:2015-10-25 1st Qu.:1.100 1st Qu.: 10839 1st Qu.: 854
## Median :2016-08-14 Median :1.370 Median : 107377 Median : 8645
## Mean :2016-08-13 Mean :1.406 Mean : 850644 Mean : 293008
## 3rd Qu.:2017-06-04 3rd Qu.:1.660 3rd Qu.: 432962 3rd Qu.: 111020
## Max. :2018-03-25 Max. :3.250 Max. :62505647 Max. :22743616
##
## X4225 X4770 Total.Bags Small.Bags
## Min. : 0 Min. : 0 Min. : 0 Min. : 0
## 1st Qu.: 3009 1st Qu.: 0 1st Qu.: 5089 1st Qu.: 2849
## Median : 29061 Median : 185 Median : 39744 Median : 26363
## Mean : 295155 Mean : 22840 Mean : 239639 Mean : 182195
## 3rd Qu.: 150207 3rd Qu.: 6243 3rd Qu.: 110783 3rd Qu.: 83338
## Max. :20470573 Max. :2546439 Max. :19373134 Max. :13384587
##
## Large.Bags XLarge.Bags type year
## Min. : 0 Min. : 0.0 conventional:9126 Min. :2015
## 1st Qu.: 127 1st Qu.: 0.0 organic :9123 1st Qu.:2015
## Median : 2648 Median : 0.0 Median :2016
## Mean : 54338 Mean : 3106.4 Mean :2016
## 3rd Qu.: 22029 3rd Qu.: 132.5 3rd Qu.:2017
## Max. :5719097 Max. :551693.7 Max. :2018
##
## region
## Albany : 338
## Atlanta : 338
## BaltimoreWashington: 338
## Boise : 338
## Boston : 338
## BuffaloRochester : 338
## (Other) :16221
Lo primero que notamos en esta base de datos con dimensiones de 13
columnas con 18,249 renglones es que tiene 3 columnas que carecen de
especificación, las cuales son X4046, X42425 y X4770, por lo que se
investigo en Kaggle y se descubrió que se refieren a tipos de aguacates,
por lo que en orden se denominan como: 1. Small Hass, 2. Large Hass y 3.
Extra Large Hass. Motivo por el cual uno de los primeros pasos será
renombrar estas columnas.
Siguiendo con el análisis nos damos cuenta que la columna región
parece tener muchos más niveles de los que muestra la función summary()
por lo que se decidió utilizar la función levels() para un mejor
entendimiento de esta.
levels(avocados$region)
## [1] "Albany" "Atlanta" "BaltimoreWashington"
## [4] "Boise" "Boston" "BuffaloRochester"
## [7] "California" "Charlotte" "Chicago"
## [10] "CincinnatiDayton" "Columbus" "DallasFtWorth"
## [13] "Denver" "Detroit" "GrandRapids"
## [16] "GreatLakes" "HarrisburgScranton" "HartfordSpringfield"
## [19] "Houston" "Indianapolis" "Jacksonville"
## [22] "LasVegas" "LosAngeles" "Louisville"
## [25] "MiamiFtLauderdale" "Midsouth" "Nashville"
## [28] "NewOrleansMobile" "NewYork" "Northeast"
## [31] "NorthernNewEngland" "Orlando" "Philadelphia"
## [34] "PhoenixTucson" "Pittsburgh" "Plains"
## [37] "Portland" "RaleighGreensboro" "RichmondNorfolk"
## [40] "Roanoke" "Sacramento" "SanDiego"
## [43] "SanFrancisco" "Seattle" "SouthCarolina"
## [46] "SouthCentral" "Southeast" "Spokane"
## [49] "StLouis" "Syracuse" "Tampa"
## [52] "TotalUS" "West" "WestTexNewMexico"
Como se puede observar, existen demasiados niveles en la columna
como para poder ser manejada de manera efectiva, por lo que consultando
el entendimiento sobre la columna de otras personas que también
trabajaron la base de datos pero que son de Estados Unidos, nos
percatamos de que esta tiene combinadas regiones generales de Estados
Unidos, como norte, sur, etc, con ciudades del país, por lo que se
dificulta el análisis si estás se mantienen combinadas. Por lo tanto, se
decidió separar la base de datos original en varias que tomen en cuenta
estas diferencias.
Nombramiento y separación
av1 <- av
av1 <- rename(av1, small_hass = "X4046", large_hass = "X4225", xl_hass = "X4770")
av1_region <- av1 %>%
filter(region %in% c("California", "West", "SouthCentral", "GreatLakes", "Midsouth", "Southeast", "Northeast", "Plains"))
# av2_region<- av1 %>%
# filter(region %in% c("California", "West", "SouthCentral", "GreatLakes", "Midsouth", "Southeast", "Northeast", "Plains"))
av1_market <- av1 %>%
filter(!(region %in% c("California", "West", "SouthCentral", "GreatLakes", "Midsouth", "Southeast", "Northeast", "Plains", "TotalUS")))
av1_total <- av1 %>%
filter(region == "TotalUS")
Análisis de precios según su tipo
Una vez separadas las regiones y ciudades de Estados Unidos, pasamos
al análisis del comportamiento de precios. Iniciando por identificar la
distribución de los precios por tipo de aguacate, creamos una tabla que
grafique la densidad de Kernel dado que un histograma normal sería más
complicado de interpretar dada la gran cantidad de precios que se
piensan graficar, y al ser un método no paramétrico no supone un riesgo
de sesgo al momento de analizar la gráfica.
ggplot(av1_region ,aes(x = AveragePrice, fill = type)) +
geom_density(alpha = 0.7) +
geom_vline(xintercept = 1.36, linetype = "dashed") +
scale_x_continuous(breaks = seq(0, 3, 0.3)) +
scale_fill_manual(values = c("#356211","#FFC324")) +
theme_minimal() +
labs(title = "Distribución de los Precios del Aguacate por Tipo",
x = "Precio Promedio",
y = "",
fill = "Tipo de Aguacate")

Como podemos ver en la gráfica, los aguacates orgánicos tienden a
ser más caros que los convencionales como era de esperarse, sin embargo,
su comportamiento en la variación de sus precios es diferente, pues
mientras que los aguacates orgánicos parecen tener una mayor variación
en sus precios, los convencionales se mantienen en su mayoría en el
mismo rango. Pero ¿es está una diferencia que afecte a la venta de
aguacates y por lo tanto a nuestras predicciones?
Ventas de aguacate según su tipo
Para identificar si las variaciones de precios entre los tipos de
aguacates tienen un efecto en sus ventas, graficamos las ventas de
aguacate divididas en tipos por los 4 años que hay en la base de
datos.
av1_region %>%
ggplot(aes(year, Total.Volume, fill = type)) +
geom_bar(width = 0.5, stat = "identity") +
scale_x_continuous(breaks = seq(2015, 2018, 1)) +
scale_y_continuous(labels = label_number(suffix = "B", scale = 1e-8)) +
scale_fill_manual(values = c("#356211", "#ffc324")) +
theme_minimal() +
labs(title = "Ventas de Aguacate",
x = "Año",
y = "Volumen de venta en Billones",
fill = "Tipo")

Como se puede apreciar, la venta de aguacates convencionales supera
por mucho las ventas del orgánico, por lo que efectivamente el que el
convencional tenga un costo menor supone una ventaja para la venta de
aguacates. Esto podría significar que para la predicción efectiva de las
ventas de aguacate, lo mejor será dividir o directamente descartar a los
aguacates orgánicos, pues podrían suponer outliers innecesarios. Otro
hallazgo de la gráfica es que la venta de aguacates no parece tener
temporalidad de tipo anual, sin embargo, esto no descarta temporalidad
de tipo mensual.
Análisis de temporalidad mensual
Para poder identificar temporalidad, vamos a graficar las ventas
totales en todo EU a través de los meses durante los 3 años y 4 meses de
los que tenemos datos.
av1_total$Date <- as.Date(av1_total$Date, "%d/%m/%Y")
ggplot(av1_total ,aes(Date, Total.Volume, color = type, group = type)) +
geom_line(size = 0.8) +
scale_x_date(date_labels = "%b-%y",
date_breaks = "3 month",
limits = c(min(date(av1_total$Date)), max(date(av1_total$Date)))) +
scale_y_continuous(breaks = waiver()) +
scale_color_manual(values = c("#356211", "#ffc324")) +
theme_minimal() +
labs(title = "Volumen de ventas por mes en todo EU",
x = "Fecha",
y = "Volumen",
color = "Tipo")
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.

Como claramente se puede apreciar en la gráfica, existe una
temporalidad en las ventas de aguacate convencional, el cual se suele
vender mucho más en febrero y decae a su punto más bajo en noviembre.
Esta temporalidad probablemente se debe al super bowl. Otro punto a
rescatar de la gráfica es que los aguacates orgánicos no cuentan con
temporalidad, lo cual es otra razón más para separarlos al momento de
hacer las predicciones.
Análisis de precios mensuales
Siguiendo con el anterior análisis, ahora graficamos de la misma
manera, pero para los precios promedios, buscando identificar
correlación con la temporalidad de ventas.
av1_total$Date <- as.Date(av1_total$Date, "%d/%m/%Y")
ggplot(av1_total ,aes(Date, AveragePrice, color = type, group = type)) +
geom_line(size = 0.8) +
scale_x_date(date_labels = "%b-%y",
date_breaks = "3 month",
limits = c(min(date(av1_total$Date)), max(date(av1_total$Date)))) +
scale_y_continuous(breaks = waiver()) +
scale_color_manual(values = c("#356211", "#ffc324")) +
theme_minimal() +
labs(title = "Precios promedios por mes en todo EU",
x = "Fecha",
y = "Precio Promedio",
color = "Tipo")

En está gráfica también podemos encontrar temporalidad, sin embargo
y a nuestro asombro, es una temporalidad contraría a la esperada, pues
en lugar de que los precios sean altos durante el superbowl cuando la
demanda es mayor, resulta que son los más bajos del año, lo que si era
esperado es que en noviembre, cuando se vende menos, los precios sean
los más altos.
Análisis de ventas por región
Como se puede ver, la región con más ventas fue West con más de 3
billones y la que tuvo menos fue la de Plains con poco menos de 1 billón
de ventas, más en este aspecto no parece haber un descubrimiento que nos
obligue a modificar la base de datos antes de hacer el modelo de
predicciones, así como también pudimos descubrir que en realidad el tipo
de bolsa o “Bag” realmente parece seguir un patrón normal donde se
distribuyen equitativamente entre todas las ventas, por lo que tampoco
suponen una anormalidad que se pueda considerar. Sin embargo, para estar
seguros ahora graficamos un mapa de volumen de ventas por
ciudad/market.
Mapa de volumen de ventas.
library(maps)
##
## Attaching package: 'maps'
## The following object is masked from 'package:purrr':
##
## map
library(mapproj)
states<- map_data("county")
names(states)[names(states) == "region"] <- "region2"
names(states)[names(states) == "subregion"] <- "region"
ar <- av1_market
ar$region<- names(ar$region)<-tolower(ar$region)
ar.geo <- merge(states,ar, sort = F, by = "region", all.x = T)
ar.geo<- ar.geo[order(ar.geo$order),]
ggplot(ar.geo, aes(long, lat))+
geom_polygon(aes(group= group, fill=Total.Volume), color= "white")+
scale_fill_continuous(
low = "blue"
,high="red", na.value = "white",
guide= "colorbar",
)+
coord_map()

ar2 <- av1_region
ar2$region<- names(ar2$region)<-tolower(ar2$region)
# Get the map data for the US states
ar.geo2 <- map_data("state")
# Merge the map data with the arrests data
ar.geo2 <- merge(ar.geo2, ar2, by.x = "region", all.x = TRUE)
# Create the plot
ggplot(ar.geo2, aes(long, lat)) +
geom_polygon(aes(group = group, fill = Total.Volume), color = "white") +
scale_fill_continuous(low = "blue", high = "red", na.value = "blue", guide = "colorbar") +
coord_map()

Modelos de predicción
Preparación de los datos
library(forecast)
## Registered S3 method overwritten by 'quantmod':
## method from
## as.zoo.data.frame zoo
# modelo de prediccion arima
ts_av <- ts(av$Total.Volume, frequency = 12, start = c(2014, 1), end = c(2100,12))
Modelo de predicción ARIMA
auto.arima(ts_av)
## Series: ts_av
## ARIMA(1,0,1) with non-zero mean
##
## Coefficients:
## ar1 ma1 mean
## 0.9778 -0.2784 809099.7
## s.e. 0.0065 0.0300 384307.5
##
## sigma^2 = 1.404e+11: log likelihood = -14879.71
## AIC=29767.43 AICc=29767.46 BIC=29787.23
model_av <- arima(ts_av, order = c(1,1,2))
summary(model_av)
##
## Call:
## arima(x = ts_av, order = c(1, 1, 2))
##
## Coefficients:
## ar1 ma1 ma2
## -0.2218 -0.0609 -0.0845
## s.e. 0.3143 0.3131 0.0926
##
## sigma^2 estimated as 1.415e+11: log likelihood = -14869.81, aic = 29747.61
##
## Training set error measures:
## ME RMSE MAE MPE MAPE MASE ACF1
## Training set 55.03804 375985 119058.9 -12.62595 25.31166 0.9633199 -0.004317561
checkresiduals(model_av)

##
## Ljung-Box test
##
## data: Residuals from ARIMA(1,1,2)
## Q* = 130.92, df = 21, p-value < 2.2e-16
##
## Model df: 3. Total lags used: 24
forecast_av <- forecast(model_av, h = 12)
plot(forecast_av, main = "Predicción de volumen total hasta 2100", xlab = "Año", ylab = "Total.Volume", xlim = c(2020, 2100), ylim = c(0, 200000), type = "l", col = "blue")

#Predicción del modelo ETS
#prediccion del modelo ets
ts_av2 <- ts(av$Total.Volume, frequency = 12, start = c(2020, 1), end = c(2030, 12))
ets_model <- ets(ts_av2)
forecast_av <- forecast(ets_model, h = 120)
plot(forecast_av, main = "Prediccion del volumen total hasta 2030", xlab = "Año", ylab = "Total.Volume")

Se utlizaron dos modelos de prediccion uno con arima y otro con la
tecnica de suavizado exponencial etc ya que creemos que estos dos
modelos son los mas optimos para predecir una serie temporal con
tendencias para la variable del volumen total y nos ayudara a poder
planear a futuro
LS0tDQp0aXRsZTogIkF2b2NhZG9zIg0KYXV0aG9yOiAiRXF1aXBvIDciDQpkYXRlOiAiMjAyMy0wMi0yMyINCm91dHB1dDogDQogIGh0bWxfZG9jdW1lbnQ6DQogICAgdG9jX2Zsb2F0OiBUcnVlDQogICAgdGhlbWU6IGRlZmF1bHQNCiAgICB0b2M6IFRydWUNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQotLS0NCg0KIyBBY3RpdmlkYWQgMi4xIEFndWFjYXRlDQoNCiMgQ29udGV4dG8NCg0KIyMjIyBMYSBiYXNlIGRlIGRhdG9zIGF2b2NhZG8uY3N2IGNvbnRpZW5lIGRhdG9zIHNvYnJlIGxhIHZlbnRhIGRlIGFndWFjYXRlcyBlbiBFc3RhZG9zIFVuaWRvcywgbG8gY3VhbCBpbmNsdXllIGRhdG9zIGNvbW8gcHJlY2lvcywgcmVnaW9uZXMsIHZvbHVtZW4gZGUgdmVudGFzLCBlbnRyZSBvdHJvcy4gTGEgaW5mb3JtYWNpw7NuIHF1ZSBzZSBidXNjYSBvYnRlbmVyIGRlIGVzdGEgYmFzZSBkZSBkYXRvcyBlcyBhbmFsaXphciBlbCBjb21wb3J0YW1pZW50byBkZSBwcmVjaW9zIHkgdmVudGFzLCBwYXJhIHBvc3Rlcmlvcm1lbnRlIG9idGVuZXIgdW4gcHJvbsOzc3RpY28gZGUgdmVudGFzLiANCg0KIyBBbsOhbGlzaXMgDQoNCiMjIExlY3R1cmEgZGUgbGEgYmFzZSBkZSBkYXRvcyANCg0KYGBge3J9DQphdiA8LSByZWFkLmNzdigiQzpcXFVzZXJzXFxhcnQxOTExMjdcXERvd25sb2Fkc1xcYXZvY2Fkby5jc3YiKQ0KYGBgDQoNCiMjIENhcmdhIGRlIGxpYnJlcsOtYXMgDQoNCmBgYHtyfQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KGx1YnJpZGF0ZSkNCmxpYnJhcnkoc2NhbGVzKQ0Kb3B0aW9ucygiaW5zdGFsbC5sb2NrIj1GQUxTRSkNCmxpYnJhcnkoZ2dtYXApDQpsaWJyYXJ5KGRwbHlyKQ0KYGBgDQoNCmBgYHtyfQ0KYXZvY2Fkb3MgPC0gYXYNCm5hbWVzKGF2b2NhZG9zKQ0KYGBgDQoNCiMjIFJlc3VtZW4gZGUgbGEgYmFzZSBkZSBkYXRvcyBhw7puIHNpbiBtb2RpZmljYXIuDQpgYGB7cn0NCm5hbWVzKGF2KVtuYW1lcyhhdikgPT0gIsOvLi5EYXRlIl0gPC0gIkRhdGUiDQpuYW1lcyhhdm9jYWRvcylbbmFtZXMoYXZvY2Fkb3MpID09ICLDry4uRGF0ZSJdIDwtICJEYXRlIg0KYGBgDQoNCmBgYHtyfQ0KYXZvY2Fkb3MkRGF0ZSA8LSBhcy5EYXRlKGF2b2NhZG9zJERhdGUsICIlZC8lbS8lWSIpDQphdm9jYWRvcyRyZWdpb24gPC0gYXMuZmFjdG9yKGF2b2NhZG9zJHJlZ2lvbikNCmF2b2NhZG9zJHR5cGUgPC0gYXMuZmFjdG9yKGF2b2NhZG9zJHR5cGUpDQpzdW1tYXJ5KGF2b2NhZG9zKQ0KYGBgDQoNCiMjIyMgTG8gcHJpbWVybyBxdWUgbm90YW1vcyBlbiBlc3RhIGJhc2UgZGUgZGF0b3MgY29uIGRpbWVuc2lvbmVzIGRlIDEzIGNvbHVtbmFzIGNvbiAxOCwyNDkgcmVuZ2xvbmVzIGVzIHF1ZSB0aWVuZSAzIGNvbHVtbmFzIHF1ZSBjYXJlY2VuIGRlIGVzcGVjaWZpY2FjacOzbiwgbGFzIGN1YWxlcyBzb24gWDQwNDYsIFg0MjQyNSB5IFg0NzcwLCBwb3IgbG8gcXVlIHNlIGludmVzdGlnbyBlbiBLYWdnbGUgeSBzZSBkZXNjdWJyacOzIHF1ZSBzZSByZWZpZXJlbiBhIHRpcG9zIGRlIGFndWFjYXRlcywgcG9yIGxvIHF1ZSBlbiBvcmRlbiBzZSBkZW5vbWluYW4gY29tbzogMS4gU21hbGwgSGFzcywgMi4gTGFyZ2UgSGFzcyB5IDMuIEV4dHJhIExhcmdlIEhhc3MuIE1vdGl2byBwb3IgZWwgY3VhbCB1bm8gZGUgbG9zIHByaW1lcm9zIHBhc29zIHNlcsOhIHJlbm9tYnJhciAgZXN0YXMgY29sdW1uYXMuIA0KDQojIyMjIFNpZ3VpZW5kbyBjb24gZWwgYW7DoWxpc2lzIG5vcyBkYW1vcyBjdWVudGEgcXVlIGxhIGNvbHVtbmEgcmVnacOzbiBwYXJlY2UgdGVuZXIgbXVjaG9zIG3DoXMgbml2ZWxlcyBkZSBsb3MgcXVlIG11ZXN0cmEgbGEgZnVuY2nDs24gc3VtbWFyeSgpIHBvciBsbyBxdWUgc2UgZGVjaWRpw7MgdXRpbGl6YXIgbGEgZnVuY2nDs24gbGV2ZWxzKCkgcGFyYSB1biBtZWpvciBlbnRlbmRpbWllbnRvIGRlIGVzdGEuDQoNCg0KDQpgYGB7cn0NCmxldmVscyhhdm9jYWRvcyRyZWdpb24pDQpgYGANCg0KIyMjIyBDb21vIHNlIHB1ZWRlIG9ic2VydmFyLCBleGlzdGVuIGRlbWFzaWFkb3Mgbml2ZWxlcyBlbiBsYSBjb2x1bW5hIGNvbW8gcGFyYSBwb2RlciBzZXIgbWFuZWphZGEgZGUgbWFuZXJhIGVmZWN0aXZhLCBwb3IgbG8gcXVlIGNvbnN1bHRhbmRvIGVsIGVudGVuZGltaWVudG8gc29icmUgbGEgY29sdW1uYSBkZSBvdHJhcyBwZXJzb25hcyBxdWUgdGFtYmnDqW4gdHJhYmFqYXJvbiBsYSBiYXNlIGRlIGRhdG9zIHBlcm8gcXVlIHNvbiBkZSBFc3RhZG9zIFVuaWRvcywgbm9zIHBlcmNhdGFtb3MgZGUgcXVlIGVzdGEgdGllbmUgY29tYmluYWRhcyByZWdpb25lcyBnZW5lcmFsZXMgZGUgRXN0YWRvcyBVbmlkb3MsIGNvbW8gbm9ydGUsIHN1ciwgZXRjLCBjb24gY2l1ZGFkZXMgZGVsIHBhw61zLCBwb3IgbG8gcXVlIHNlIGRpZmljdWx0YSBlbCBhbsOhbGlzaXMgc2kgZXN0w6FzIHNlIG1hbnRpZW5lbiBjb21iaW5hZGFzLiBQb3IgbG8gdGFudG8sIHNlIGRlY2lkacOzIHNlcGFyYXIgbGEgYmFzZSBkZSBkYXRvcyBvcmlnaW5hbCBlbiB2YXJpYXMgcXVlIHRvbWVuIGVuIGN1ZW50YSBlc3RhcyBkaWZlcmVuY2lhcy4gDQoNCiMjIE5vbWJyYW1pZW50byB5IHNlcGFyYWNpw7NuDQoNCmBgYHtyfQ0KYXYxIDwtIGF2DQoNCmF2MSA8LSByZW5hbWUoYXYxLCBzbWFsbF9oYXNzID0gIlg0MDQ2IiwgbGFyZ2VfaGFzcyA9ICJYNDIyNSIsIHhsX2hhc3MgPSAiWDQ3NzAiKQ0KDQphdjFfcmVnaW9uIDwtIGF2MSAlPiUNCiAgZmlsdGVyKHJlZ2lvbiAlaW4lIGMoIkNhbGlmb3JuaWEiLCAiV2VzdCIsICJTb3V0aENlbnRyYWwiLCAiR3JlYXRMYWtlcyIsICJNaWRzb3V0aCIsICJTb3V0aGVhc3QiLCAiTm9ydGhlYXN0IiwgIlBsYWlucyIpKQ0KDQogIyBhdjJfcmVnaW9uPC0gYXYxICU+JQ0KICMgICBmaWx0ZXIocmVnaW9uICVpbiUgYygiQ2FsaWZvcm5pYSIsICJXZXN0IiwgIlNvdXRoQ2VudHJhbCIsICJHcmVhdExha2VzIiwgIk1pZHNvdXRoIiwgIlNvdXRoZWFzdCIsICJOb3J0aGVhc3QiLCAiUGxhaW5zIikpDQoNCmF2MV9tYXJrZXQgPC0gYXYxICU+JQ0KICBmaWx0ZXIoIShyZWdpb24gJWluJSBjKCJDYWxpZm9ybmlhIiwgIldlc3QiLCAiU291dGhDZW50cmFsIiwgIkdyZWF0TGFrZXMiLCAiTWlkc291dGgiLCAiU291dGhlYXN0IiwgIk5vcnRoZWFzdCIsICJQbGFpbnMiLCAiVG90YWxVUyIpKSkNCg0KYXYxX3RvdGFsIDwtIGF2MSAlPiUNCiAgZmlsdGVyKHJlZ2lvbiA9PSAiVG90YWxVUyIpDQpgYGANCg0KIyMgQW7DoWxpc2lzIGRlIHByZWNpb3Mgc2Vnw7puIHN1IHRpcG8NCg0KIyMjIyBVbmEgdmV6IHNlcGFyYWRhcyBsYXMgcmVnaW9uZXMgeSBjaXVkYWRlcyBkZSBFc3RhZG9zIFVuaWRvcywgcGFzYW1vcyBhbCBhbsOhbGlzaXMgZGVsIGNvbXBvcnRhbWllbnRvIGRlIHByZWNpb3MuIEluaWNpYW5kbyBwb3IgaWRlbnRpZmljYXIgbGEgZGlzdHJpYnVjacOzbiBkZSBsb3MgcHJlY2lvcyBwb3IgdGlwbyBkZSBhZ3VhY2F0ZSwgY3JlYW1vcyB1bmEgdGFibGEgcXVlIGdyYWZpcXVlIGxhIGRlbnNpZGFkIGRlIEtlcm5lbCBkYWRvIHF1ZSB1biBoaXN0b2dyYW1hIG5vcm1hbCBzZXLDrWEgbcOhcyBjb21wbGljYWRvIGRlIGludGVycHJldGFyIGRhZGEgbGEgZ3JhbiBjYW50aWRhZCBkZSBwcmVjaW9zIHF1ZSBzZSBwaWVuc2FuIGdyYWZpY2FyLCB5IGFsIHNlciB1biBtw6l0b2RvIG5vIHBhcmFtw6l0cmljbyBubyBzdXBvbmUgdW4gcmllc2dvIGRlIHNlc2dvIGFsIG1vbWVudG8gZGUgYW5hbGl6YXIgbGEgZ3LDoWZpY2EuIA0KDQpgYGB7cn0NCmdncGxvdChhdjFfcmVnaW9uICxhZXMoeCA9IEF2ZXJhZ2VQcmljZSwgZmlsbCA9IHR5cGUpKSArDQogIGdlb21fZGVuc2l0eShhbHBoYSA9IDAuNykgKw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAxLjM2LCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArDQpzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDMsIDAuMykpICsNCnNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIiMzNTYyMTEiLCIjRkZDMzI0IikpICsNCnRoZW1lX21pbmltYWwoKSArDQpsYWJzKHRpdGxlID0gIkRpc3RyaWJ1Y2nDs24gZGUgbG9zIFByZWNpb3MgZGVsIEFndWFjYXRlIHBvciBUaXBvIiwNCiAgICAgICB4ID0gIlByZWNpbyBQcm9tZWRpbyIsDQogICAgICAgeSA9ICIiLA0KICAgICAgIGZpbGwgPSAiVGlwbyBkZSBBZ3VhY2F0ZSIpIA0KYGBgDQoNCiMjIyMgQ29tbyBwb2RlbW9zIHZlciBlbiBsYSBncsOhZmljYSwgbG9zIGFndWFjYXRlcyBvcmfDoW5pY29zIHRpZW5kZW4gYSBzZXIgbcOhcyBjYXJvcyBxdWUgbG9zIGNvbnZlbmNpb25hbGVzIGNvbW8gZXJhIGRlIGVzcGVyYXJzZSwgc2luIGVtYmFyZ28sIHN1IGNvbXBvcnRhbWllbnRvIGVuIGxhIHZhcmlhY2nDs24gZGUgc3VzIHByZWNpb3MgZXMgZGlmZXJlbnRlLCBwdWVzIG1pZW50cmFzIHF1ZSBsb3MgYWd1YWNhdGVzIG9yZ8Ohbmljb3MgcGFyZWNlbiB0ZW5lciB1bmEgbWF5b3IgdmFyaWFjacOzbiBlbiBzdXMgcHJlY2lvcywgbG9zIGNvbnZlbmNpb25hbGVzIHNlIG1hbnRpZW5lbiBlbiBzdSBtYXlvcsOtYSBlbiBlbCBtaXNtbyByYW5nby4gUGVybyDCv2VzIGVzdMOhIHVuYSBkaWZlcmVuY2lhIHF1ZSBhZmVjdGUgYSBsYSB2ZW50YSBkZSBhZ3VhY2F0ZXMgeSBwb3IgbG8gdGFudG8gYSBudWVzdHJhcyBwcmVkaWNjaW9uZXM/DQoNCiMjIFZlbnRhcyBkZSBhZ3VhY2F0ZSBzZWfDum4gc3UgdGlwbw0KDQojIyMjIFBhcmEgaWRlbnRpZmljYXIgc2kgbGFzIHZhcmlhY2lvbmVzIGRlIHByZWNpb3MgZW50cmUgbG9zIHRpcG9zIGRlIGFndWFjYXRlcyB0aWVuZW4gdW4gZWZlY3RvIGVuIHN1cyB2ZW50YXMsIGdyYWZpY2Ftb3MgbGFzIHZlbnRhcyBkZSBhZ3VhY2F0ZSBkaXZpZGlkYXMgZW4gdGlwb3MgcG9yIGxvcyA0IGHDsW9zIHF1ZSBoYXkgZW4gbGEgYmFzZSBkZSBkYXRvcy4gDQoNCmBgYHtyfQ0KYXYxX3JlZ2lvbiAlPiUNCmdncGxvdChhZXMoeWVhciwgVG90YWwuVm9sdW1lLCBmaWxsID0gdHlwZSkpICsgDQogICAgZ2VvbV9iYXIod2lkdGggPSAwLjUsIHN0YXQgPSAiaWRlbnRpdHkiKSArDQpzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDIwMTUsIDIwMTgsIDEpKSArDQpzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gbGFiZWxfbnVtYmVyKHN1ZmZpeCA9ICJCIiwgc2NhbGUgPSAxZS04KSkgKw0Kc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiIzM1NjIxMSIsICIjZmZjMzI0IikpICsNCnRoZW1lX21pbmltYWwoKSArDQpsYWJzKHRpdGxlID0gIlZlbnRhcyBkZSBBZ3VhY2F0ZSIsDQogICAgICAgeCA9ICJBw7FvIiwNCiAgICAgICB5ID0gIlZvbHVtZW4gZGUgdmVudGEgZW4gQmlsbG9uZXMiLA0KICAgICAgIGZpbGwgPSAiVGlwbyIpDQpgYGANCg0KIyMjIyBDb21vIHNlIHB1ZWRlIGFwcmVjaWFyLCBsYSB2ZW50YSBkZSBhZ3VhY2F0ZXMgY29udmVuY2lvbmFsZXMgc3VwZXJhIHBvciBtdWNobyBsYXMgdmVudGFzIGRlbCBvcmfDoW5pY28sIHBvciBsbyBxdWUgZWZlY3RpdmFtZW50ZSBlbCBxdWUgZWwgY29udmVuY2lvbmFsIHRlbmdhIHVuIGNvc3RvIG1lbm9yIHN1cG9uZSB1bmEgdmVudGFqYSBwYXJhIGxhIHZlbnRhIGRlIGFndWFjYXRlcy4gRXN0byBwb2Ryw61hIHNpZ25pZmljYXIgcXVlIHBhcmEgbGEgcHJlZGljY2nDs24gZWZlY3RpdmEgZGUgbGFzIHZlbnRhcyBkZSBhZ3VhY2F0ZSwgbG8gbWVqb3Igc2Vyw6EgZGl2aWRpciBvIGRpcmVjdGFtZW50ZSBkZXNjYXJ0YXIgYSBsb3MgYWd1YWNhdGVzIG9yZ8Ohbmljb3MsIHB1ZXMgcG9kcsOtYW4gc3Vwb25lciBvdXRsaWVycyBpbm5lY2VzYXJpb3MuIE90cm8gaGFsbGF6Z28gZGUgbGEgZ3LDoWZpY2EgZXMgcXVlIGxhIHZlbnRhIGRlIGFndWFjYXRlcyBubyBwYXJlY2UgdGVuZXIgdGVtcG9yYWxpZGFkIGRlIHRpcG8gYW51YWwsIHNpbiBlbWJhcmdvLCBlc3RvIG5vIGRlc2NhcnRhIHRlbXBvcmFsaWRhZCBkZSB0aXBvIG1lbnN1YWwuIA0KDQojIyBBbsOhbGlzaXMgZGUgdGVtcG9yYWxpZGFkIG1lbnN1YWwNCg0KIyMjIyBQYXJhIHBvZGVyIGlkZW50aWZpY2FyIHRlbXBvcmFsaWRhZCwgdmFtb3MgYSBncmFmaWNhciBsYXMgdmVudGFzIHRvdGFsZXMgZW4gdG9kbyBFVSBhIHRyYXbDqXMgZGUgbG9zIG1lc2VzIGR1cmFudGUgbG9zIDMgYcOxb3MgeSA0IG1lc2VzIGRlIGxvcyBxdWUgdGVuZW1vcyBkYXRvcy4NCg0KYGBge3J9DQphdjFfdG90YWwkRGF0ZSA8LSBhcy5EYXRlKGF2MV90b3RhbCREYXRlLCAiJWQvJW0vJVkiKQ0KDQpnZ3Bsb3QoYXYxX3RvdGFsICxhZXMoRGF0ZSwgVG90YWwuVm9sdW1lLCBjb2xvciA9IHR5cGUsIGdyb3VwID0gdHlwZSkpICsNCiAgICBnZW9tX2xpbmUoc2l6ZSA9IDAuOCkgKw0Kc2NhbGVfeF9kYXRlKGRhdGVfbGFiZWxzID0gIiViLSV5IiwNCiAgICAgICAgICAgICAgIGRhdGVfYnJlYWtzID0gICIzIG1vbnRoIiwNCiAgICAgICAgICAgICAgIGxpbWl0cyA9IGMobWluKGRhdGUoYXYxX3RvdGFsJERhdGUpKSwgbWF4KGRhdGUoYXYxX3RvdGFsJERhdGUpKSkpICsNCnNjYWxlX3lfY29udGludW91cyhicmVha3MgPSB3YWl2ZXIoKSkgKw0Kc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIiMzNTYyMTEiLCAiI2ZmYzMyNCIpKSArDQp0aGVtZV9taW5pbWFsKCkgKw0KbGFicyh0aXRsZSA9ICJWb2x1bWVuIGRlIHZlbnRhcyBwb3IgbWVzIGVuIHRvZG8gRVUiLA0KICAgICAgIHggPSAiRmVjaGEiLA0KICAgICAgIHkgPSAiVm9sdW1lbiIsDQogICAgICAgY29sb3IgPSAiVGlwbyIpIA0KYGBgDQoNCiMjIyMgQ29tbyBjbGFyYW1lbnRlIHNlIHB1ZWRlIGFwcmVjaWFyIGVuIGxhIGdyw6FmaWNhLCBleGlzdGUgdW5hIHRlbXBvcmFsaWRhZCBlbiBsYXMgdmVudGFzIGRlIGFndWFjYXRlIGNvbnZlbmNpb25hbCwgZWwgY3VhbCBzZSBzdWVsZSB2ZW5kZXIgbXVjaG8gbcOhcyBlbiBmZWJyZXJvIHkgZGVjYWUgYSBzdSBwdW50byBtw6FzIGJham8gZW4gbm92aWVtYnJlLiBFc3RhIHRlbXBvcmFsaWRhZCBwcm9iYWJsZW1lbnRlIHNlIGRlYmUgYWwgc3VwZXIgYm93bC4gT3RybyBwdW50byBhIHJlc2NhdGFyIGRlIGxhIGdyw6FmaWNhIGVzIHF1ZSBsb3MgYWd1YWNhdGVzIG9yZ8Ohbmljb3Mgbm8gY3VlbnRhbiBjb24gdGVtcG9yYWxpZGFkLCBsbyBjdWFsIGVzIG90cmEgcmF6w7NuIG3DoXMgcGFyYSBzZXBhcmFybG9zIGFsIG1vbWVudG8gZGUgaGFjZXIgbGFzIHByZWRpY2Npb25lcy4gDQoNCiMjIEFuw6FsaXNpcyBkZSBwcmVjaW9zIG1lbnN1YWxlcw0KDQojIyMjIFNpZ3VpZW5kbyBjb24gZWwgYW50ZXJpb3IgYW7DoWxpc2lzLCBhaG9yYSBncmFmaWNhbW9zIGRlIGxhIG1pc21hIG1hbmVyYSwgcGVybyBwYXJhIGxvcyBwcmVjaW9zIHByb21lZGlvcywgYnVzY2FuZG8gaWRlbnRpZmljYXIgY29ycmVsYWNpw7NuIGNvbiBsYSB0ZW1wb3JhbGlkYWQgZGUgdmVudGFzLiANCg0KYGBge3J9DQphdjFfdG90YWwkRGF0ZSA8LSBhcy5EYXRlKGF2MV90b3RhbCREYXRlLCAiJWQvJW0vJVkiKQ0KDQpnZ3Bsb3QoYXYxX3RvdGFsICxhZXMoRGF0ZSwgQXZlcmFnZVByaWNlLCBjb2xvciA9IHR5cGUsIGdyb3VwID0gdHlwZSkpICsNCiAgICBnZW9tX2xpbmUoc2l6ZSA9IDAuOCkgKw0Kc2NhbGVfeF9kYXRlKGRhdGVfbGFiZWxzID0gIiViLSV5IiwNCiAgICAgICAgICAgICAgIGRhdGVfYnJlYWtzID0gICIzIG1vbnRoIiwNCiAgICAgICAgICAgICAgIGxpbWl0cyA9IGMobWluKGRhdGUoYXYxX3RvdGFsJERhdGUpKSwgbWF4KGRhdGUoYXYxX3RvdGFsJERhdGUpKSkpICsNCnNjYWxlX3lfY29udGludW91cyhicmVha3MgPSB3YWl2ZXIoKSkgKw0Kc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIiMzNTYyMTEiLCAiI2ZmYzMyNCIpKSArDQp0aGVtZV9taW5pbWFsKCkgKw0KbGFicyh0aXRsZSA9ICJQcmVjaW9zIHByb21lZGlvcyBwb3IgbWVzIGVuIHRvZG8gRVUiLA0KICAgICAgIHggPSAiRmVjaGEiLA0KICAgICAgIHkgPSAiUHJlY2lvIFByb21lZGlvIiwNCiAgICAgICBjb2xvciA9ICJUaXBvIikgDQpgYGANCg0KIyMjIyBFbiBlc3TDoSBncsOhZmljYSB0YW1iacOpbiBwb2RlbW9zIGVuY29udHJhciB0ZW1wb3JhbGlkYWQsIHNpbiBlbWJhcmdvIHkgYSBudWVzdHJvIGFzb21icm8sIGVzIHVuYSB0ZW1wb3JhbGlkYWQgY29udHJhcsOtYSBhIGxhIGVzcGVyYWRhLCBwdWVzIGVuIGx1Z2FyIGRlIHF1ZSBsb3MgcHJlY2lvcyBzZWFuIGFsdG9zIGR1cmFudGUgZWwgc3VwZXJib3dsIGN1YW5kbyBsYSBkZW1hbmRhIGVzIG1heW9yLCByZXN1bHRhIHF1ZSBzb24gbG9zIG3DoXMgYmFqb3MgZGVsIGHDsW8sIGxvIHF1ZSBzaSBlcmEgZXNwZXJhZG8gZXMgcXVlIGVuIG5vdmllbWJyZSwgY3VhbmRvIHNlIHZlbmRlIG1lbm9zLCBsb3MgcHJlY2lvcyBzZWFuIGxvcyBtw6FzIGFsdG9zLg0KDQojIyBBbsOhbGlzaXMgZGUgdmVudGFzIHBvciByZWdpw7NuDQoNCiMjIyMgU2lndWllbmRvIGNvbiBlbCBhbsOhbGlzaXMgZGUgbG9zIGRhdG9zLCBhaG9yYSBwYXNhbW9zIGEgbGFzIHZlbnRhcyBkZSBhZ3VhY2F0ZSBwb3IgcmVnacOzbi4gQWRlbcOhcywgYSBlc3RlIGFuw6FsaXNpcyBzZSBsZSB2YSBpbmNsdWlyIGxhIGRpc3RyaWJ1Y2nDs24gZGUgZXN0YXMgdmVudGFzIGVuIGxvcyBkaWZlcmVudGVzIHRpcG9zIGRlICJCYWdzIiBxdWUgdmllbmVuIGVuIGxhIGJhc2UgZGUgZGF0b3MsIHBhcmEgZXN0bywgc2UgdHJhbnNmb3JtYXJvbiBsYXMgY29sdW1uYXMgZGUgIkJhZ3MiIGEgZmlsYXMgY29uIHN1cyByZXNwZWN0aXZvcyBuw7ptZXJvcyBhIHRyYXbDqXMgZGUgbGEgZnVuY2nDs24gcGl2b3RfbG9uZ2VyKCkNCg0KYGBge3J9DQphdjEgPC0gYXYxICU+JQ0KICBwaXZvdF9sb25nZXIoYyhTbWFsbC5CYWdzLCBMYXJnZS5CYWdzLCBYTGFyZ2UuQmFncyksIG5hbWVzX3RvID0gImJhZ19zaXplIiwgdmFsdWVzX3RvID0gImJhZ190b3RhbCIpDQpgYGANCg0KYGBge3IsIGVjaG89RkFMU0V9DQojIyBOTyBNT1NUUkFSDQphdjFfcmVnaW9uIDwtIGF2MSAlPiUNCiAgZmlsdGVyKHJlZ2lvbiAlaW4lIGMoIkNhbGlmb3JuaWEiLCAiV2VzdCIsICJTb3V0aENlbnRyYWwiLCAiR3JlYXRMYWtlcyIsICJNaWRzb3V0aCIsICJTb3V0aGVhc3QiLCAiTm9ydGhlYXN0IiwgIlBsYWlucyIpKSANCg0KYXYxX21hcmtldCA8LSBhdjEgJT4lDQogIGZpbHRlcighKHJlZ2lvbiAlaW4lIGMoIkNhbGlmb3JuaWEiLCAiV2VzdCIsICJTb3V0aENlbnRyYWwiLCAiR3JlYXRMYWtlcyIsICJNaWRzb3V0aCIsICJTb3V0aGVhc3QiLCAiTm9ydGhlYXN0IiwgIlBsYWlucyIsICJUb3RhbFVTIikpKQ0KDQphdjFfdG90YWwgPC0gYXYxICU+JQ0KICBmaWx0ZXIocmVnaW9uID09ICJUb3RhbFVTIikNCmBgYA0KDQoNCmBgYHtyfQ0KZ2dwbG90KGF2MV9yZWdpb24sIGFlcyhyZW9yZGVyKHJlZ2lvbiwgVG90YWwuVm9sdW1lKSwgVG90YWwuVm9sdW1lLCBmaWxsID0gYmFnX3NpemUpKSArDQpnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5Iiwgd2lkdGggPSAwLjcpICsNCnNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBsYWJlbF9udW1iZXIoc3VmZml4ID0gIkIiLCBzY2FsZSA9IDFlLTkpKSArDQpzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCIjMzU2MjExIiwgIiNmZmMzMjQiLCAiI2UzNjU1YiIsICIjM2EzMzM1IikpICsNCmNvb3JkX2ZsaXAoKSArDQp0aGVtZV9taW5pbWFsKCkgKw0KbGFicyh0aXRsZSA9ICJWZW50YXMgZGUgYWd1YWNhdGUgcG9yIHJlZ2nDs24geSBiYWciLA0KICAgICAgIHggPSAiUmVnacOzbiIsDQogICAgICAgeSA9ICJWb2x1bWVuIGRlIHZlbnRhcyBlbiBCaWxsb25lcyIsDQogICAgICAgZmlsbCA9ICJUaXBvIGRlIEJhZyIpDQpgYGANCg0KIyMjIyBDb21vIHNlIHB1ZWRlIHZlciwgbGEgcmVnacOzbiBjb24gbcOhcyB2ZW50YXMgZnVlIFdlc3QgY29uIG3DoXMgZGUgMyBiaWxsb25lcyB5IGxhIHF1ZSB0dXZvIG1lbm9zIGZ1ZSBsYSBkZSBQbGFpbnMgY29uIHBvY28gbWVub3MgZGUgMSBiaWxsw7NuIGRlIHZlbnRhcywgbcOhcyBlbiBlc3RlIGFzcGVjdG8gbm8gcGFyZWNlIGhhYmVyIHVuIGRlc2N1YnJpbWllbnRvIHF1ZSBub3Mgb2JsaWd1ZSBhIG1vZGlmaWNhciBsYSBiYXNlIGRlIGRhdG9zIGFudGVzIGRlIGhhY2VyIGVsIG1vZGVsbyBkZSBwcmVkaWNjaW9uZXMsIGFzw60gY29tbyB0YW1iacOpbiBwdWRpbW9zIGRlc2N1YnJpciBxdWUgZW4gcmVhbGlkYWQgZWwgdGlwbyBkZSBib2xzYSBvICJCYWciIHJlYWxtZW50ZSBwYXJlY2Ugc2VndWlyIHVuIHBhdHLDs24gbm9ybWFsIGRvbmRlIHNlIGRpc3RyaWJ1eWVuIGVxdWl0YXRpdmFtZW50ZSBlbnRyZSB0b2RhcyBsYXMgdmVudGFzLCBwb3IgbG8gcXVlIHRhbXBvY28gc3Vwb25lbiB1bmEgYW5vcm1hbGlkYWQgcXVlIHNlIHB1ZWRhIGNvbnNpZGVyYXIuIFNpbiBlbWJhcmdvLCBwYXJhIGVzdGFyIHNlZ3Vyb3MgYWhvcmEgZ3JhZmljYW1vcyB1biBtYXBhIGRlIHZvbHVtZW4gZGUgdmVudGFzIHBvciBjaXVkYWQvbWFya2V0Lg0KDQojIE1hcGEgZGUgdm9sdW1lbiBkZSB2ZW50YXMuIA0KYGBge3J9DQpsaWJyYXJ5KG1hcHMpDQpsaWJyYXJ5KG1hcHByb2opDQoNCmBgYA0KDQpgYGB7cn0NCnN0YXRlczwtIG1hcF9kYXRhKCJjb3VudHkiKQ0KIG5hbWVzKHN0YXRlcylbbmFtZXMoc3RhdGVzKSA9PSAicmVnaW9uIl0gPC0gInJlZ2lvbjIiDQogbmFtZXMoc3RhdGVzKVtuYW1lcyhzdGF0ZXMpID09ICJzdWJyZWdpb24iXSA8LSAicmVnaW9uIg0KYXIgPC0gYXYxX21hcmtldA0KDQoNCg0KYXIkcmVnaW9uPC0gbmFtZXMoYXIkcmVnaW9uKTwtdG9sb3dlcihhciRyZWdpb24pDQoNCg0KDQpgYGANCg0KDQoNCg0KYGBge3J9DQphci5nZW8gPC0gbWVyZ2Uoc3RhdGVzLGFyLCBzb3J0ID0gRiwgYnkgPSAicmVnaW9uIiwgYWxsLnggPSBUKQ0KYXIuZ2VvPC0gYXIuZ2VvW29yZGVyKGFyLmdlbyRvcmRlciksXQ0KYGBgDQoNCg0KYGBge3J9DQpnZ3Bsb3QoYXIuZ2VvLCBhZXMobG9uZywgbGF0KSkrDQogIGdlb21fcG9seWdvbihhZXMoZ3JvdXA9IGdyb3VwLCBmaWxsPVRvdGFsLlZvbHVtZSksIGNvbG9yPSAid2hpdGUiKSsNCiAgc2NhbGVfZmlsbF9jb250aW51b3VzKA0KICAgIGxvdyA9ICJibHVlIg0KICAgICAgLGhpZ2g9InJlZCIsICBuYS52YWx1ZSA9ICJ3aGl0ZSIsDQogICAgZ3VpZGU9ICJjb2xvcmJhciIsDQogICAgDQogICkrDQogIGNvb3JkX21hcCgpDQpgYGANCg0KDQpgYGB7cn0NCmFyMiA8LSBhdjFfcmVnaW9uDQoNCmFyMiRyZWdpb248LSBuYW1lcyhhcjIkcmVnaW9uKTwtdG9sb3dlcihhcjIkcmVnaW9uKQ0KDQojIEdldCB0aGUgbWFwIGRhdGEgZm9yIHRoZSBVUyBzdGF0ZXMNCmFyLmdlbzIgPC0gbWFwX2RhdGEoInN0YXRlIikNCg0KIyBNZXJnZSB0aGUgbWFwIGRhdGEgd2l0aCB0aGUgYXJyZXN0cyBkYXRhDQphci5nZW8yIDwtIG1lcmdlKGFyLmdlbzIsIGFyMiwgYnkueCA9ICJyZWdpb24iLCBhbGwueCA9IFRSVUUpDQoNCiMgQ3JlYXRlIHRoZSBwbG90DQpnZ3Bsb3QoYXIuZ2VvMiwgYWVzKGxvbmcsIGxhdCkpICsNCiAgZ2VvbV9wb2x5Z29uKGFlcyhncm91cCA9IGdyb3VwLCBmaWxsID0gVG90YWwuVm9sdW1lKSwgY29sb3IgPSAid2hpdGUiKSArDQogIHNjYWxlX2ZpbGxfY29udGludW91cyhsb3cgPSAiYmx1ZSIsIGhpZ2ggPSAicmVkIiwgbmEudmFsdWUgPSAiYmx1ZSIsIGd1aWRlID0gImNvbG9yYmFyIikgKw0KICBjb29yZF9tYXAoKQ0KYGBgDQoNCg0KDQoNCg0KIyBNb2RlbG9zIGRlIHByZWRpY2Npw7NuDQoNCiMjIFByZXBhcmFjacOzbiBkZSBsb3MgZGF0b3MNCg0KYGBge3J9DQpsaWJyYXJ5KGZvcmVjYXN0KQ0KIyBtb2RlbG8gZGUgcHJlZGljY2lvbiBhcmltYQ0KdHNfYXYgPC0gdHMoYXYkVG90YWwuVm9sdW1lLCBmcmVxdWVuY3kgPSAxMiwgc3RhcnQgPSBjKDIwMTQsIDEpLCBlbmQgPSBjKDIxMDAsMTIpKQ0KYGBgDQoNCiMjIE1vZGVsbyBkZSBwcmVkaWNjacOzbiBBUklNQQ0KDQpgYGB7cn0NCmF1dG8uYXJpbWEodHNfYXYpDQoNCm1vZGVsX2F2IDwtIGFyaW1hKHRzX2F2LCBvcmRlciA9IGMoMSwxLDIpKQ0KDQpzdW1tYXJ5KG1vZGVsX2F2KQ0KDQpjaGVja3Jlc2lkdWFscyhtb2RlbF9hdikNCg0KZm9yZWNhc3RfYXYgPC0gZm9yZWNhc3QobW9kZWxfYXYsIGggPSAxMikNCg0KcGxvdChmb3JlY2FzdF9hdiwgbWFpbiA9ICJQcmVkaWNjacOzbiBkZSB2b2x1bWVuIHRvdGFsIGhhc3RhIDIxMDAiLCB4bGFiID0gIkHDsW8iLCB5bGFiID0gIlRvdGFsLlZvbHVtZSIsIHhsaW0gPSBjKDIwMjAsIDIxMDApLCB5bGltID0gYygwLCAyMDAwMDApLCB0eXBlID0gImwiLCBjb2wgPSAiYmx1ZSIpDQpgYGANCg0KDQojUHJlZGljY2nDs24gZGVsIG1vZGVsbyBFVFMNCg0KYGBge3J9DQojcHJlZGljY2lvbiBkZWwgbW9kZWxvIGV0cyANCg0KdHNfYXYyIDwtIHRzKGF2JFRvdGFsLlZvbHVtZSwgZnJlcXVlbmN5ID0gMTIsIHN0YXJ0ID0gYygyMDIwLCAxKSwgZW5kID0gYygyMDMwLCAxMikpDQogDQpldHNfbW9kZWwgPC0gZXRzKHRzX2F2MikNCg0KZm9yZWNhc3RfYXYgPC0gZm9yZWNhc3QoZXRzX21vZGVsLCBoID0gMTIwKQ0KDQpwbG90KGZvcmVjYXN0X2F2LCBtYWluID0gIlByZWRpY2Npb24gZGVsIHZvbHVtZW4gdG90YWwgaGFzdGEgMjAzMCIsIHhsYWIgPSAiQcOxbyIsIHlsYWIgPSAiVG90YWwuVm9sdW1lIikNCmBgYA0KDQoNCiMjIyMgU2UgdXRsaXphcm9uIGRvcyBtb2RlbG9zIGRlIHByZWRpY2Npb24gdW5vIGNvbiBhcmltYSB5IG90cm8gY29uIGxhIHRlY25pY2EgZGUgc3Vhdml6YWRvIGV4cG9uZW5jaWFsIGV0YyB5YSBxdWUgY3JlZW1vcyBxdWUgZXN0b3MgZG9zIG1vZGVsb3Mgc29uIGxvcyBtYXMgb3B0aW1vcyBwYXJhIHByZWRlY2lyIHVuYSBzZXJpZSB0ZW1wb3JhbCBjb24gdGVuZGVuY2lhcyBwYXJhIGxhIHZhcmlhYmxlIGRlbCB2b2x1bWVuIHRvdGFsIHkgbm9zIGF5dWRhcmEgYSBwb2RlciBwbGFuZWFyIGEgZnV0dXJvDQoNCg0KIyMgDQoNCg0KDQo=