Actividad 2.1 Aguacate

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

Siguiendo con el análisis de los datos, ahora pasamos a las ventas de aguacate por región. Además, a este análisis se le va incluir la distribución de estas ventas en los diferentes tipos de “Bags” que vienen en la base de datos, para esto, se transformaron las columnas de “Bags” a filas con sus respectivos números a través de la función pivot_longer()

av1 <- av1 %>%
  pivot_longer(c(Small.Bags, Large.Bags, XLarge.Bags), names_to = "bag_size", values_to = "bag_total")
ggplot(av1_region, aes(reorder(region, Total.Volume), Total.Volume, fill = bag_size)) +
geom_bar(stat = "identity", width = 0.7) +
scale_y_continuous(labels = label_number(suffix = "B", scale = 1e-9)) +
scale_fill_manual(values = c("#356211", "#ffc324", "#e3655b", "#3a3335")) +
coord_flip() +
theme_minimal() +
labs(title = "Ventas de aguacate por región y bag",
       x = "Región",
       y = "Volumen de ventas en Billones",
       fill = "Tipo de Bag")

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=