Alumno: Rossi, Santiago

Cargo las librerias

library(tidyverse)
library(knitr)
library(kableExtra)
library(ggplot2)
library(GGally)
library(ggthemes)

1.Preparacion de los datos (I)

Cargo el dataset

properati<-read_rds(path = "ar_properati.rds")
write_rds(properati,"ar_properati.rds")

El dataset de Properati cuenta información de avisos inmobiliarios para cuatro países (Argentina, Brasil, Uruguay y EEUU). Entre las principales variables del dataset se destacan: fecha de al aviso, longitud y latitud de la propiedad, localidad, barrio, tipo de propiedad, cantidad de habitaciones, metros cuadrados cubiertos y totales y precio.

glimpse(properati)
Observations: 388,891
Variables: 24
$ id              <fct> S0we3z3V2JpHUJreqQ2t/w==, kMxcmAS8NvrynGBVbMOEaQ==, Ce3ojF+ZTOkB8d+LI9dpxg==, AUGpj3raGmOCiulSM...
$ ad_type         <fct> Propiedad, Propiedad, Propiedad, Propiedad, Propiedad, Propiedad, Propiedad, Propiedad, Propied...
$ start_date      <fct> 2019-04-14, 2019-04-14, 2019-04-14, 2019-04-14, 2019-04-14, 2019-04-14, 2019-04-14, 2019-04-14,...
$ end_date        <fct> 2019-06-14, 2019-04-16, 9999-12-31, 9999-12-31, 2019-07-09, 2019-08-08, 2019-07-10, 2019-06-14,...
$ created_on      <fct> 2019-04-14, 2019-04-14, 2019-04-14, 2019-04-14, 2019-04-14, 2019-04-14, 2019-04-14, 2019-04-14,...
$ lat             <dbl> -34.94331, -34.63181, NA, -34.65471, -34.65495, -32.93547, -34.65183, -34.91213, -34.60338, NA,...
$ lon             <dbl> -54.92966, -58.42060, NA, -58.79089, -58.78712, -60.68398, -58.65912, -54.84749, -58.43450, NA,...
$ l1              <fct> Uruguay, Argentina, Argentina, Argentina, Argentina, Argentina, Argentina, Uruguay, Argentina, ...
$ l2              <fct> Maldonado, Capital Federal, Bs.As. G.B.A. Zona Norte, Bs.As. G.B.A. Zona Oeste, Bs.As. G.B.A. Z...
$ l3              <fct> Punta del Este, Boedo, NA, Moreno, Moreno, Rosario, Ituzaingó, José Ignacio, Almagro, Miramar, ...
$ l4              <fct> NA, NA, NA, Moreno, Moreno, NA, Ituzaingó, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, ...
$ l5              <fct> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,...
$ l6              <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,...
$ rooms           <int> 2, NA, 2, 2, 2, 4, NA, 6, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 1, 1, 1, 1, 1, 1, 1, ...
$ bedrooms        <int> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,...
$ bathrooms       <int> 1, NA, 1, 2, 3, 1, 3, 3, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, 1, NA, 1, 1, 1, 1, 1, 1, 1, 1,...
$ surface_total   <int> 45, NA, 200, 460, 660, NA, 70, NA, 1300, 405, 352, 373, 360, 1325, 250, 80142, 101, NA, 54, 180...
$ surface_covered <int> 40, NA, NA, 100, 148, 89, 122, NA, NA, NA, NA, NA, NA, 2, NA, NA, NA, NA, 54, 180, 33, 33, 34, ...
$ price           <int> 13000, 0, NA, NA, NA, NA, NA, NA, 0, NA, 0, NA, NA, NA, NA, NA, 0, 0, 0, NA, 0, 0, 0, 0, 0, 0, ...
$ currency        <fct> UYU, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA...
$ price_period    <fct> Mensual, Mensual, NA, Mensual, Mensual, Mensual, Mensual, Mensual, Mensual, Mensual, Mensual, M...
$ title           <fct> Departamento - Roosevelt, PH - Boedo, Ituzaingo  1100 - $ 1 - Casa Alquiler, Dr. Vera   300 - C...
$ property_type   <fct> Departamento, PH, Casa, Casa, Casa, Casa, Casa, Casa, Lote, Lote, Lote, Lote, Lote, Lote, Lote,...
$ operation_type  <fct> Alquiler, Venta, Alquiler, Venta, Venta, Venta, Venta, Alquiler, Venta, Venta, Venta, Venta, Ve...
summary(properati)
                        id              ad_type            start_date           end_date           created_on    
 ///dr7MC645N/4gZ6eXuUg==:     1   Propiedad:388891   2019-05-31: 10567   9999-12-31:117295   2019-05-31: 10567  
 //+CYRxKFmd756CnDA/cqA==:     1                      2019-04-29:  6121   2019-06-23: 34915   2019-04-29:  6121  
 //+tP3jysTzlcccJ3QSc0w==:     1                      2019-01-16:  6102   2019-06-08: 15091   2019-01-16:  6102  
 //0A/RLg2X1acCjxTk7lXQ==:     1                      2019-06-01:  4749   2019-07-10: 13981   2019-06-01:  4749  
 //17Mk/t87M5tdc6l/hwVw==:     1                      2019-06-10:  4712   2019-07-19:  8640   2019-06-10:  4712  
 //2nJwFGlRu5WFZQ3szAMQ==:     1                      2019-05-03:  4592   2019-06-14:  7381   2019-05-03:  4592  
 (Other)                 :388885                      (Other)   :352048   (Other)   :191588   (Other)   :352048  
      lat              lon                       l1                                    l2                     l3        
 Min.   :-54.98   Min.   :-105.27   Argentina     :374977   Capital Federal             :124327   Rosario      : 29634  
 1st Qu.:-34.67   1st Qu.: -58.80   Brasil        :    34   Bs.As. G.B.A. Zona Norte    : 72088   Tigre        : 23421  
 Median :-34.60   Median : -58.48   Estados Unidos:   305   Santa Fe                    : 36199   Mar del Plata: 23075  
 Mean   :-34.48   Mean   : -59.37   Uruguay       : 13575   Buenos Aires Costa Atlántica: 30649   Palermo      : 18907  
 3rd Qu.:-34.43   3rd Qu.: -58.40                           Bs.As. G.B.A. Zona Oeste    : 28576   Córdoba      : 14006  
 Max.   : 44.67   Max.   : -41.90                           Bs.As. G.B.A. Zona Sur      : 28080   (Other)      :267077  
 NA's   :50597    NA's   :50597                             (Other)                     : 68972   NA's         : 12771  
           l4                         l5            l6              rooms           bedrooms        bathrooms    
 Nordelta   : 10696   Barrio El Golf   :   400   Mode:logical   Min.   : 1.0     Min.   : -2.00   Min.   : 1.00  
 La Plata   :  4225   BarrioPortezuelo :   400   NA's:388891    1st Qu.: 2.0     1st Qu.:  1.00   1st Qu.: 1.00  
 Ramos Mejía:  3753   Barrio Los Alisos:   230                  Median : 3.0     Median :  2.00   Median : 1.00  
 Olivos     :  2597   Barrio Los Lagos :   173                  Mean   : 2.9     Mean   :  2.16   Mean   : 1.67  
 San Isidro :  2474   Islas del Canal  :   138                  3rd Qu.: 4.0     3rd Qu.:  3.00   3rd Qu.: 2.00  
 (Other)    : 91766   (Other)          :  1065                  Max.   :40.0     Max.   :390.00   Max.   :20.00  
 NA's       :273380   NA's             :386485                  NA's   :144668   NA's   :230747   NA's   :94136  
 surface_total      surface_covered       price           currency       price_period   
 Min.   :    -3.0   Min.   :   -139   Min.   :0.000e+00   ARS :116131   Diario :    49  
 1st Qu.:    50.0   1st Qu.:     43   1st Qu.:2.000e+04   PEN :   123   Mensual:227625  
 Median :    91.0   Median :     70   Median :9.400e+04   USD :242465   Semanal:    28  
 Mean   :   458.3   Mean   :    235   Mean   :2.690e+05   UYU :  1178   NA's   :161189  
 3rd Qu.:   266.0   3rd Qu.:    142   3rd Qu.:2.250e+05   NA's: 28994                   
 Max.   :200000.0   Max.   :4000000   Max.   :2.147e+09                                 
 NA's   :74063      NA's   :97854     NA's   :21222                                     
                                 title                property_type              operation_type  
 DEPARTAMENTO EN VENTA              : 11559   Departamento   :188588   Alquiler         :104263  
 DEPARTAMENTO EN ALQUILER           :  7115   Casa           : 84117   Alquiler temporal: 15815  
 CASA EN VENTA                      :  5298   Lote           : 45724   Venta            :268813  
 Departamento en Alquiler Temporario:  2364   PH             : 19340                             
 LOTE EN VENTA                      :  2340   Local comercial: 19225                             
 PH EN VENTA                        :  2214   Oficina        : 13516                             
 (Other)                            :358001   (Other)        : 18381                             

Filtro variables seleccionadas: país = Argentina, ciudad= CABA, tipo de operación = venta, tipo de propiedad y moneda de la operación = dólares.

properati<-properati %>% 
  filter(l1=="Argentina" & l2=="Capital Federal" &
                                  currency=="USD" & (property_type=="PH" | property_type=="Departamento" |
                                                     property_type=="Casa") & operation_type=="Venta") %>%
  select(id,l3,rooms,bedrooms,bathrooms,surface_total,surface_covered,price,property_type)
properati %>% 
  head() %>% 
  kable() %>% 
  kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"))
id l3 rooms bedrooms bathrooms surface_total surface_covered price property_type
oyj+f764ALCYodIqBvWAww== Barracas NA NA NA 300 180 320000 PH
HdjpKrqdwYfH9YU1DKjltg== Boedo 6 NA 2 178 240 500000 Casa
YwWE3rTb2+gmsBwjUHmAPQ== Palermo NA NA 2 240 157 350000 Casa
6AxnSWOhbIU8TUCqb+paBg== Belgrano 3 NA 4 157 NA 470000 Casa
U4fk+co3Rd8JDMot0pQI6Q== Versalles NA NA 1 140 110 155000 Casa
AfdcsqUSelai1ofCAq2B0Q== Velez Sarsfield 3 NA 2 95 69 199900 Casa

2.Analisis exploratorio (I)

2.a. Cantidad de valores unicos y de valores faltantes (NAs)

Calculo la cantidad de valores unicos por variable.

#Funciones para el cálculo de faltantes y valores únicos
sumo_na<-function(x){  sum(is.na(x))}
cant_unicos<-function(x){  length(unique(x))}
#Cantidad de valores únicos 
properati %>% summarise_all(list( cant_unicos= cant_unicos)) %>% gather()

Calculo la cantidad de valores faltantes por variable. La variable bedrooms tiene una elevada presentcia de datos faltantes.

#Cantidad de faltantes 
properati %>% summarise_all(list(cant_na = sumo_na, cant_unicos= cant_unicos)) %>% gather()
#Grafico Faltantes
properati %>% summarise_all(list(cant_na = sumo_na)) %>% gather() %>% ggplot(.,aes(x=reorder(key,-value), y= value))+ geom_bar(stat = "identity")+  labs(title = "Cantidad de faltates por variable") +
labs(x = "Nº faltantes", y = "Variables") + theme( axis.text.x = element_text(colour = "black", size = 10, angle = 90),
axis.text.y = element_text(colour = "black", size = 10))

Matriz de correlacion para las variables numericas.

Se encuentra que las varaibles bedrooms y rooms registran una elevada correlación lineal (0.92).

properati %>%
  select(-id,-l3,-property_type) %>% 
  cor(., use="complete.obs", method="pearson")
                     rooms   bedrooms  bathrooms surface_total surface_covered      price
rooms           1.00000000 0.92138719 0.61335026    0.06828238      0.07468335 0.48748747
bedrooms        0.92138719 1.00000000 0.61578024    0.06746895      0.07206826 0.43221753
bathrooms       0.61335026 0.61578024 1.00000000    0.06234262      0.06777010 0.59904254
surface_total   0.06828238 0.06746895 0.06234262    1.00000000      0.69656225 0.05095265
surface_covered 0.07468335 0.07206826 0.06777010    0.69656225      1.00000000 0.06257960
price           0.48748747 0.43221753 0.59904254    0.05095265      0.06257960 1.00000000

3. Preparacion de los datos (II)

Elimino variable bedrooms por tener una proporción grande de faltantes y tener una elevada correlación con rooms. Asimismo, me quedo con aquellos registros completos.

properati<-properati %>% select(-bedrooms) %>% filter(complete.cases(.))

4. Analisis exploratorios (II)

4.a.Estadísticas descriptivas para la variable precio

summary(properati$price)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   6000  119000  170000  251577  270000 6000000 

Los precios presentan una distribución log-normal

ggplot(properati, aes(x = price)) + geom_histogram(bins = 40, col="#4d4d4d", fill="#d8b365")+theme_tufte(base_size = 15)+ggtitle("Distribución de precios de inmuebles")+xlab("Precio (USD)")+ylab("Frecuencia")

ggplot(properati, aes(y = price,fill=property_type)) + geom_boxplot()+theme_tufte(base_size = 15) + labs(title = "Precios de inmuebles por tipo de vivienda", subtitle = "en dólares")+xlab("")+ylab("Precios (USD)")+labs(fill = "Tipo de propiedad")+
theme(plot.title = element_text(size = 14, family = "Tahoma", face = "bold"),
      panel.border = element_rect(colour = "black", fill = NA, size = .5),
      legend.position = "bottom")

  
#geom_boxplot(outlier.shape = NA)
properati %>%
  select(-id,-l3,property_type) %>% 
ggpairs(., 
        title = "Matriz de correlaciones",
        mapping = aes(colour= property_type))

5. Outliers

Se tomo como criterio para definir como outlier a aquellos valores que superaban en 2.5 el rango intercuartil. De esta forma se considerarán como outliers al 5% de los datos

#Funciones para identificar valores atípicos
lim_sup_precio <- function(data, tipo_vivienda){data %>% filter(property_type==tipo_vivienda) %>% summarise( l_sup =  quantile(price,probs = 0.75, names = F) +2.5*IQR(price)) }
lim_inf_precio <- function(data, tipo_vivienda){data %>% filter(property_type==tipo_vivienda) %>% summarise( l_sup =  quantile(price,probs = 0.25,names = F) -2.5*IQR(price))}
#Genero variable 'oulier' que toma valor 1 si es considerado valor extremo
properati <- properati %>%
  mutate(outlier = ifelse( (property_type=="Casa" & price > lim_sup_precio(.,"Casa")[[1]]) |
                           (property_type=="Casa" & price < lim_inf_precio(.,"Casa")[[1]]) |
                           (property_type=="PH" & price > lim_sup_precio(.,"PH")[[1]]) |
                           (property_type=="PH" & price < lim_inf_precio(.,"PH")[[1]]) | 
                           (property_type=="Departamento" & price > lim_sup_precio(.,"Departamento")[[1]]) |
                           (property_type=="Departamento" & price < lim_inf_precio(.,"Departamento")[[1]]),1,0 ))
#Genero data frame 'properati 1' que no contiene calores extremos
properati_1<-properati %>% filter(outlier==0)
glue::glue("Proporción de outliers = ", sum(properati$outlier)/nrow(properati))
Proporción de outliers = 0.0518648701425503

6.Analisis exploratorios (III)

ggplot(properati_1, aes(x = price)) + geom_histogram(bins = 40, col="#4d4d4d", fill="#d8b365")+theme_tufte(base_size = 15)+ggtitle("Distribución de precios de inmuebles")+xlab("Precio (USD)")+ylab("Frecuencia")

A continuación se presenta el box-plot de los precios por tipo de propiedad. De observar el gráfico se desprende que: - las casas tienen una mayor dispersión de precios que los otros tipos de vivienda. - las casas son en promedio más caras que los PH y los departamentos. - Los PHs son en promedio más caros que os departamentos.

ggplot(properati_1, aes(y = price,fill=property_type)) + geom_boxplot()+theme_tufte(base_size = 15) + labs(title = "Precios de inmuebles por tipo de vivienda", subtitle = "en dólares")+xlab("")+ylab("Precios (USD)")+ylim(0,9.5e+05)+labs(fill = "Tipo de propiedad")+
theme(plot.title = element_text(size = 14, family = "Tahoma", face = "bold"),
      panel.border = element_rect(colour = "black", fill = NA, size = .5),
      legend.position = "bottom")

properati_1 %>%
  select(-id,-l3,property_type) %>% 
ggpairs(., 
        title = "Matriz de correlaciones",
        mapping = aes(colour= property_type))

7.Modelo lineal

Realizar un modelo lineal simple para explicar el precio en función de las habitaciones (rooms) y otro modelo que explique el precio en función de la superficie total (surface_total)

Usar la función summary() para obtener informacion de ambos modelos. Explicar los valores de los coeficientes estimados. ¿Cuál modelo usarían para predecir el precio? ¿Por qué?

Modelo I: precio(rooms)

El modelo I busca explicar los precios como una función lineal de la cantidad de habitaciones. El coeficiente que vincula estas variables es positivo y significativo con un p-value <2e-16. Asimismo, el cofieciente (58.857) puede interpretarse como: por cada habitación el precio de la viviendase incrementa en 58 mil dólares.

El R cuadrado indica que el modelo logra explicar un 40% de la variabilidad del precio.

modelo_rooms<-lm(data = properati_1,formula = price ~ rooms)
summary(modelo_rooms)

Call:
lm(formula = price ~ rooms, data = properati_1)

Residuals:
     Min       1Q   Median       3Q      Max 
-1325720   -55007   -14256    31136   763422 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  42292.6      977.8   43.25   <2e-16 ***
rooms        58857.1      327.0  180.00   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 93440 on 48552 degrees of freedom
Multiple R-squared:  0.4002,    Adjusted R-squared:  0.4002 
F-statistic: 3.24e+04 on 1 and 48552 DF,  p-value: < 2.2e-16
ggplot(properati_1,mapping = aes(x=price, y = rooms))+geom_point()+geom_smooth(method = "lm")+ labs(title = "Cantidad de habitaciones y precios de inmuebles", subtitle = "")+xlab("Precio (USD)")+ylab("Cat. Habitaciones")+theme_bw()+
theme(plot.title = element_text(size = 14, face = "bold"),
      panel.border = element_rect(colour = "black", fill = NA, size = .5))

Modelo II: precio(suface-total)

El modelo II busca explicar los precios como una función a la superficie total. La presencia de outliers hace que lo coeficientes estén mal estimados.

por esta razón se reetimo el modelo quitando las viviendas con más de 1500 metros cuadrados.

modelo_surface<-lm(data = properati_1, price ~ surface_total)
summary(modelo_surface)

Call:
lm(formula = price ~ surface_total, data = properati_1)

Residuals:
    Min      1Q  Median      3Q     Max 
-984524  -85458  -36029   49212  896735 

Coefficients:
               Estimate Std. Error t value Pr(>|t|)    
(Intercept)   2.002e+05  5.501e+02  363.84   <2e-16 ***
surface_total 7.928e+00  6.549e-01   12.11   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 120500 on 48552 degrees of freedom
Multiple R-squared:  0.003009,  Adjusted R-squared:  0.002988 
F-statistic: 146.5 on 1 and 48552 DF,  p-value: < 2.2e-16
ggplot(properati_1,mapping = aes(x=price, y = surface_total))+geom_point()+geom_smooth(method = "lm")+ labs(title = "Superficie total y precios de inmuebles", subtitle = "")+xlab("Precio (USD)")+ylab("Superficie (m2)")+theme_bw()+
theme(plot.title = element_text(size = 14, face = "bold"),
      panel.border = element_rect(colour = "black", fill = NA, size = .5))

Modelo II sin ouliers: precio(suface-total)

El modelo corregido registra coeficientes positivo y significativos al 1%. Asimismo, el cofieciente (1269.6) que asocia la superfcie al precio puede interpretarse como: por cada metro cuadrado de superficie el precio de la viviendase incrementa en 1270 dólares.

El R cuadrado indica que el modelo logra explicar apenas un 48% de la variabilidad del precio.

modelo_surface<-lm(data = properati_1[properati_1$surface_total<1500,], price ~ surface_total)
summary(modelo_surface)

Call:
lm(formula = price ~ surface_total, data = properati_1[properati_1$surface_total < 
    1500, ])

Residuals:
     Min       1Q   Median       3Q      Max 
-1237352   -47463   -19109    31386   599196 

Coefficients:
               Estimate Std. Error t value Pr(>|t|)    
(Intercept)   96011.045    629.819   152.4   <2e-16 ***
surface_total  1269.611      5.951   213.3   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 86640 on 48502 degrees of freedom
Multiple R-squared:  0.4841,    Adjusted R-squared:  0.4841 
F-statistic: 4.551e+04 on 1 and 48502 DF,  p-value: < 2.2e-16
ggplot(properati_1[properati_1$surface_total<1500,],mapping = aes(x=price, y = surface_total))+geom_point()+geom_smooth(method = "lm")+ labs(title = "Superficie total y precios de inmuebles", subtitle = "")+xlab("Precio (USD)")+ylab("Superficie (m2)")+theme_bw()+
theme(plot.title = element_text(size = 14, face = "bold"),
      panel.border = element_rect(colour = "black", fill = NA, size = .5))

Selección del modelo

Dado que el R cuadrado es superior en el segundo modelo, se puede argumentar que este modelo logra explicar una mayor proporción de la variabilida y por lo tanto resulta superior al modelo de habitaciones.

LS0tDQp0aXRsZTogIlRQMSBBbmFsaXNpcyBleHBsb3JhdG9yaW8gZSBJbnRyb2R1Y2Npb24gYSBSZWdyZXNpw7NuIGxpbmVhbCINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCiMjQWx1bW5vOiBSb3NzaSwgU2FudGlhZ28gDQoNCkNhcmdvIGxhcyBsaWJyZXJpYXMNCmBgYHtyLCBldmFsPUZBTFNFfQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KGtuaXRyKQ0KbGlicmFyeShrYWJsZUV4dHJhKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShHR2FsbHkpDQpsaWJyYXJ5KGdndGhlbWVzKQ0KYGBgDQoNCiMxLlByZXBhcmFjaW9uIGRlIGxvcyBkYXRvcyAoSSkgDQoNCkNhcmdvIGVsIGRhdGFzZXQNCg0KYGBge3J9DQpwcm9wZXJhdGk8LXJlYWRfcmRzKHBhdGggPSAiYXJfcHJvcGVyYXRpLnJkcyIpDQoNCmBgYA0KDQpgYGB7cn0NCiN3cml0ZV9yZHMocHJvcGVyYXRpLCJhcl9wcm9wZXJhdGkucmRzIikNCmBgYA0KDQoNCg0KRWwgZGF0YXNldCBkZSBQcm9wZXJhdGkgY3VlbnRhIGluZm9ybWFjacOzbiBkZSBhdmlzb3MgaW5tb2JpbGlhcmlvcyBwYXJhIGN1YXRybyBwYcOtc2VzIChBcmdlbnRpbmEsIEJyYXNpbCwgVXJ1Z3VheSB5IEVFVVUpLiBFbnRyZSBsYXMgcHJpbmNpcGFsZXMgdmFyaWFibGVzIGRlbCBkYXRhc2V0IHNlIGRlc3RhY2FuOiBmZWNoYSBkZSBhbCBhdmlzbywgbG9uZ2l0dWQgeSBsYXRpdHVkIGRlIGxhIHByb3BpZWRhZCwgbG9jYWxpZGFkLCBiYXJyaW8sIHRpcG8gZGUgcHJvcGllZGFkLCBjYW50aWRhZCBkZSBoYWJpdGFjaW9uZXMsIG1ldHJvcyBjdWFkcmFkb3MgY3ViaWVydG9zIHkgdG90YWxlcyB5IHByZWNpby4NCg0KYGBge3IsIHdhcm5pbmc9RkFMU0V9DQoNCmdsaW1wc2UocHJvcGVyYXRpKQ0KDQpzdW1tYXJ5KHByb3BlcmF0aSkNCg0KYGBgDQogDQpGaWx0cm8gdmFyaWFibGVzIHNlbGVjY2lvbmFkYXM6IHBhw61zID0gQXJnZW50aW5hLCBjaXVkYWQ9IENBQkEsIHRpcG8gZGUgb3BlcmFjacOzbiA9IHZlbnRhLCB0aXBvIGRlIHByb3BpZWRhZCB5IG1vbmVkYSBkZSBsYSBvcGVyYWNpw7NuID0gZMOzbGFyZXMuDQoNCmBgYHtyfQ0KcHJvcGVyYXRpPC1wcm9wZXJhdGkgJT4lIA0KICBmaWx0ZXIobDE9PSJBcmdlbnRpbmEiICYgbDI9PSJDYXBpdGFsIEZlZGVyYWwiICYNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjdXJyZW5jeT09IlVTRCIgJiAocHJvcGVydHlfdHlwZT09IlBIIiB8IHByb3BlcnR5X3R5cGU9PSJEZXBhcnRhbWVudG8iIHwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvcGVydHlfdHlwZT09IkNhc2EiKSAmIG9wZXJhdGlvbl90eXBlPT0iVmVudGEiKSAlPiUNCiAgc2VsZWN0KGlkLGwzLHJvb21zLGJlZHJvb21zLGJhdGhyb29tcyxzdXJmYWNlX3RvdGFsLHN1cmZhY2VfY292ZXJlZCxwcmljZSxwcm9wZXJ0eV90eXBlKQ0KDQpwcm9wZXJhdGkgJT4lIA0KICBoZWFkKCkgJT4lIA0KICBrYWJsZSgpICU+JSANCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiLCAiY29uZGVuc2VkIiwgInJlc3BvbnNpdmUiKSkNCmBgYA0KDQojMi5BbmFsaXNpcyBleHBsb3JhdG9yaW8gKEkpIA0KDQojIyMyLmEuIENhbnRpZGFkIGRlIHZhbG9yZXMgdW5pY29zIHkgZGUgdmFsb3JlcyBmYWx0YW50ZXMgKE5BcykNCg0KQ2FsY3VsbyBsYSBjYW50aWRhZCBkZSB2YWxvcmVzIHVuaWNvcyBwb3IgdmFyaWFibGUuDQoNCmBgYHtyfQ0KI0Z1bmNpb25lcyBwYXJhIGVsIGPDoWxjdWxvIGRlIGZhbHRhbnRlcyB5IHZhbG9yZXMgw7puaWNvcw0Kc3Vtb19uYTwtZnVuY3Rpb24oeCl7ICBzdW0oaXMubmEoeCkpfQ0KY2FudF91bmljb3M8LWZ1bmN0aW9uKHgpeyAgbGVuZ3RoKHVuaXF1ZSh4KSl9DQoNCg0KI0NhbnRpZGFkIGRlIHZhbG9yZXMgw7puaWNvcyANCnByb3BlcmF0aSAlPiUgc3VtbWFyaXNlX2FsbChsaXN0KCBjYW50X3VuaWNvcz0gY2FudF91bmljb3MpKSAlPiUgZ2F0aGVyKCkNCg0KYGBgDQoNCkNhbGN1bG8gbGEgY2FudGlkYWQgZGUgdmFsb3JlcyBmYWx0YW50ZXMgcG9yIHZhcmlhYmxlLiBMYSB2YXJpYWJsZSBiZWRyb29tcyB0aWVuZSB1bmEgZWxldmFkYSBwcmVzZW50Y2lhIGRlIGRhdG9zIGZhbHRhbnRlcy4NCg0KYGBge3J9DQojQ2FudGlkYWQgZGUgZmFsdGFudGVzIA0KcHJvcGVyYXRpICU+JSBzdW1tYXJpc2VfYWxsKGxpc3QoY2FudF9uYSA9IHN1bW9fbmEsIGNhbnRfdW5pY29zPSBjYW50X3VuaWNvcykpICU+JSBnYXRoZXIoKQ0KDQojR3JhZmljbyBGYWx0YW50ZXMNCnByb3BlcmF0aSAlPiUgc3VtbWFyaXNlX2FsbChsaXN0KGNhbnRfbmEgPSBzdW1vX25hKSkgJT4lIGdhdGhlcigpICU+JSBnZ3Bsb3QoLixhZXMoeD1yZW9yZGVyKGtleSwtdmFsdWUpLCB5PSB2YWx1ZSkpKyBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikrICBsYWJzKHRpdGxlID0gIkNhbnRpZGFkIGRlIGZhbHRhdGVzIHBvciB2YXJpYWJsZSIpICsNCmxhYnMoeCA9ICJOwrogZmFsdGFudGVzIiwgeSA9ICJWYXJpYWJsZXMiKSArIHRoZW1lKCBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChjb2xvdXIgPSAiYmxhY2siLCBzaXplID0gMTAsIGFuZ2xlID0gOTApLA0KYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoY29sb3VyID0gImJsYWNrIiwgc2l6ZSA9IDEwKSkNCmBgYA0KDQoNCiMjIyBNYXRyaXogZGUgY29ycmVsYWNpb24gcGFyYSBsYXMgdmFyaWFibGVzIG51bWVyaWNhcy4gDQoNClNlIGVuY3VlbnRyYSBxdWUgbGFzIHZhcmFpYmxlcyBiZWRyb29tcyB5IHJvb21zIHJlZ2lzdHJhbiB1bmEgZWxldmFkYSBjb3JyZWxhY2nDs24gbGluZWFsICgwLjkyKS4NCmBgYHtyfQ0KcHJvcGVyYXRpICU+JQ0KICBzZWxlY3QoLWlkLC1sMywtcHJvcGVydHlfdHlwZSkgJT4lIA0KICBjb3IoLiwgdXNlPSJjb21wbGV0ZS5vYnMiLCBtZXRob2Q9InBlYXJzb24iKQ0KYGBgICAgICAgDQoNCg0KIzMuIFByZXBhcmFjaW9uIGRlIGxvcyBkYXRvcyAoSUkpDQpFbGltaW5vIHZhcmlhYmxlIGBiZWRyb29tc2AgcG9yIHRlbmVyIHVuYSBwcm9wb3JjacOzbiBncmFuZGUgZGUgZmFsdGFudGVzIHkgdGVuZXIgdW5hIGVsZXZhZGEgY29ycmVsYWNpw7NuIGNvbiBgcm9vbXNgLiBBc2ltaXNtbywgbWUgcXVlZG8gY29uIGFxdWVsbG9zIHJlZ2lzdHJvcyBjb21wbGV0b3MuDQoNCmBgYHtyfQ0KcHJvcGVyYXRpPC1wcm9wZXJhdGkgJT4lIHNlbGVjdCgtYmVkcm9vbXMpICU+JSBmaWx0ZXIoY29tcGxldGUuY2FzZXMoLikpDQpgYGAgIA0KDQojNC4gQW5hbGlzaXMgZXhwbG9yYXRvcmlvcyAoSUkpDQoNCiMjNC5hLkVzdGFkw61zdGljYXMgZGVzY3JpcHRpdmFzIHBhcmEgbGEgdmFyaWFibGUgcHJlY2lvDQoNCmBgYHtyfQ0Kc3VtbWFyeShwcm9wZXJhdGkkcHJpY2UpDQpgYGANCg0KTG9zIHByZWNpb3MgcHJlc2VudGFuIHVuYSAgZGlzdHJpYnVjacOzbiBsb2ctbm9ybWFsDQoNCmBgYHtyfQ0KZ2dwbG90KHByb3BlcmF0aSwgYWVzKHggPSBwcmljZSkpICsgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDQwLCBjb2w9IiM0ZDRkNGQiLCBmaWxsPSIjZDhiMzY1IikrdGhlbWVfdHVmdGUoYmFzZV9zaXplID0gMTUpK2dndGl0bGUoIkRpc3RyaWJ1Y2nDs24gZGUgcHJlY2lvcyBkZSBpbm11ZWJsZXMiKSt4bGFiKCJQcmVjaW8gKFVTRCkiKSt5bGFiKCJGcmVjdWVuY2lhIikNCmBgYA0KDQpgYGB7ciwgd2FybmluZz1GQUxTRX0NCmdncGxvdChwcm9wZXJhdGksIGFlcyh5ID0gcHJpY2UsZmlsbD1wcm9wZXJ0eV90eXBlKSkgKyBnZW9tX2JveHBsb3QoKSt0aGVtZV90dWZ0ZShiYXNlX3NpemUgPSAxNSkgKyBsYWJzKHRpdGxlID0gIlByZWNpb3MgZGUgaW5tdWVibGVzIHBvciB0aXBvIGRlIHZpdmllbmRhIiwgc3VidGl0bGUgPSAiZW4gZMOzbGFyZXMiKSt4bGFiKCIiKSt5bGFiKCJQcmVjaW9zIChVU0QpIikrbGFicyhmaWxsID0gIlRpcG8gZGUgcHJvcGllZGFkIikrDQp0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgZmFtaWx5ID0gIlRhaG9tYSIsIGZhY2UgPSAiYm9sZCIpLA0KICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJibGFjayIsIGZpbGwgPSBOQSwgc2l6ZSA9IC41KSwNCiAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQ0KICANCmBgYA0KDQpgYGB7ciwgd2FybmluZz1GQUxTRX0NCnByb3BlcmF0aSAlPiUNCiAgc2VsZWN0KC1pZCwtbDMscHJvcGVydHlfdHlwZSkgJT4lIA0KDQpnZ3BhaXJzKC4sIA0KICAgICAgICB0aXRsZSA9ICJNYXRyaXogZGUgY29ycmVsYWNpb25lcyIsDQogICAgICAgIG1hcHBpbmcgPSBhZXMoY29sb3VyPSBwcm9wZXJ0eV90eXBlKSkNCmBgYA0KDQoNCiM1LiBPdXRsaWVycw0KDQpTZSB0b21vIGNvbW8gY3JpdGVyaW8gcGFyYSBkZWZpbmlyIGNvbW8gb3V0bGllciBhIGFxdWVsbG9zIHZhbG9yZXMgcXVlIHN1cGVyYWJhbiBlbiAyLjUgZWwgcmFuZ28gaW50ZXJjdWFydGlsLiBEZSBlc3RhIGZvcm1hIHNlIGNvbnNpZGVyYXLDoW4gY29tbyBvdXRsaWVycyBhbCA1JSBkZSBsb3MgZGF0b3MNCg0KYGBge3J9DQoNCiNGdW5jaW9uZXMgcGFyYSBpZGVudGlmaWNhciB2YWxvcmVzIGF0w61waWNvcw0KbGltX3N1cF9wcmVjaW8gPC0gZnVuY3Rpb24oZGF0YSwgdGlwb192aXZpZW5kYSl7ZGF0YSAlPiUgZmlsdGVyKHByb3BlcnR5X3R5cGU9PXRpcG9fdml2aWVuZGEpICU+JSBzdW1tYXJpc2UoIGxfc3VwID0gIHF1YW50aWxlKHByaWNlLHByb2JzID0gMC43NSwgbmFtZXMgPSBGKSArMi41KklRUihwcmljZSkpIH0NCg0KbGltX2luZl9wcmVjaW8gPC0gZnVuY3Rpb24oZGF0YSwgdGlwb192aXZpZW5kYSl7ZGF0YSAlPiUgZmlsdGVyKHByb3BlcnR5X3R5cGU9PXRpcG9fdml2aWVuZGEpICU+JSBzdW1tYXJpc2UoIGxfc3VwID0gIHF1YW50aWxlKHByaWNlLHByb2JzID0gMC4yNSxuYW1lcyA9IEYpIC0yLjUqSVFSKHByaWNlKSl9DQoNCiNHZW5lcm8gdmFyaWFibGUgJ291bGllcicgcXVlIHRvbWEgdmFsb3IgMSBzaSBlcyBjb25zaWRlcmFkbyB2YWxvciBleHRyZW1vDQpwcm9wZXJhdGkgPC0gcHJvcGVyYXRpICU+JQ0KICBtdXRhdGUob3V0bGllciA9IGlmZWxzZSggKHByb3BlcnR5X3R5cGU9PSJDYXNhIiAmIHByaWNlID4gbGltX3N1cF9wcmVjaW8oLiwiQ2FzYSIpW1sxXV0pIHwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIChwcm9wZXJ0eV90eXBlPT0iQ2FzYSIgJiBwcmljZSA8IGxpbV9pbmZfcHJlY2lvKC4sIkNhc2EiKVtbMV1dKSB8DQogICAgICAgICAgICAgICAgICAgICAgICAgICAocHJvcGVydHlfdHlwZT09IlBIIiAmIHByaWNlID4gbGltX3N1cF9wcmVjaW8oLiwiUEgiKVtbMV1dKSB8DQogICAgICAgICAgICAgICAgICAgICAgICAgICAocHJvcGVydHlfdHlwZT09IlBIIiAmIHByaWNlIDwgbGltX2luZl9wcmVjaW8oLiwiUEgiKVtbMV1dKSB8IA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgKHByb3BlcnR5X3R5cGU9PSJEZXBhcnRhbWVudG8iICYgcHJpY2UgPiBsaW1fc3VwX3ByZWNpbyguLCJEZXBhcnRhbWVudG8iKVtbMV1dKSB8DQogICAgICAgICAgICAgICAgICAgICAgICAgICAocHJvcGVydHlfdHlwZT09IkRlcGFydGFtZW50byIgJiBwcmljZSA8IGxpbV9pbmZfcHJlY2lvKC4sIkRlcGFydGFtZW50byIpW1sxXV0pLDEsMCApKQ0KDQojR2VuZXJvIGRhdGEgZnJhbWUgJ3Byb3BlcmF0aSAxJyBxdWUgbm8gY29udGllbmUgY2Fsb3JlcyBleHRyZW1vcw0KcHJvcGVyYXRpXzE8LXByb3BlcmF0aSAlPiUgZmlsdGVyKG91dGxpZXI9PTApDQoNCmdsdWU6OmdsdWUoIlByb3BvcmNpw7NuIGRlIG91dGxpZXJzID0gIiwgc3VtKHByb3BlcmF0aSRvdXRsaWVyKS9ucm93KHByb3BlcmF0aSkpDQoNCmBgYA0KDQoNCiM2LkFuYWxpc2lzIGV4cGxvcmF0b3Jpb3MgKElJSSkNCmBgYHtyfQ0KZ2dwbG90KHByb3BlcmF0aV8xLCBhZXMoeCA9IHByaWNlKSkgKyBnZW9tX2hpc3RvZ3JhbShiaW5zID0gNDAsIGNvbD0iIzRkNGQ0ZCIsIGZpbGw9IiNkOGIzNjUiKSt0aGVtZV90dWZ0ZShiYXNlX3NpemUgPSAxNSkrZ2d0aXRsZSgiRGlzdHJpYnVjacOzbiBkZSBwcmVjaW9zIGRlIGlubXVlYmxlcyIpK3hsYWIoIlByZWNpbyAoVVNEKSIpK3lsYWIoIkZyZWN1ZW5jaWEiKQ0KYGBgDQoNCkEgY29udGludWFjacOzbiBzZSBwcmVzZW50YSBlbCBib3gtcGxvdCBkZSBsb3MgcHJlY2lvcyBwb3IgdGlwbyBkZSBwcm9waWVkYWQuICBEZSBvYnNlcnZhciBlbCBncsOhZmljbyBzZSBkZXNwcmVuZGUgcXVlOg0KLSBsYXMgY2FzYXMgdGllbmVuIHVuYSBtYXlvciBkaXNwZXJzacOzbiBkZSBwcmVjaW9zIHF1ZSBsb3Mgb3Ryb3MgdGlwb3MgZGUgdml2aWVuZGEuDQotIGxhcyBjYXNhcyBzb24gZW4gcHJvbWVkaW8gbcOhcyBjYXJhcyBxdWUgbG9zIFBIIHkgbG9zIGRlcGFydGFtZW50b3MuDQotIExvcyBQSHMgc29uIGVuIHByb21lZGlvIG3DoXMgY2Fyb3MgcXVlIG9zIGRlcGFydGFtZW50b3MuDQoNCmBgYHtyLCB3YXJuaW5nPUZBTFNFfQ0KZ2dwbG90KHByb3BlcmF0aV8xLCBhZXMoeSA9IHByaWNlLGZpbGw9cHJvcGVydHlfdHlwZSkpICsgZ2VvbV9ib3hwbG90KCkrdGhlbWVfdHVmdGUoYmFzZV9zaXplID0gMTUpICsgbGFicyh0aXRsZSA9ICJQcmVjaW9zIGRlIGlubXVlYmxlcyBwb3IgdGlwbyBkZSB2aXZpZW5kYSIsIHN1YnRpdGxlID0gImVuIGTDs2xhcmVzIikreGxhYigiIikreWxhYigiUHJlY2lvcyAoVVNEKSIpK3lsaW0oMCw5LjVlKzA1KStsYWJzKGZpbGwgPSAiVGlwbyBkZSBwcm9waWVkYWQiKSsNCnRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBmYW1pbHkgPSAiVGFob21hIiwgZmFjZSA9ICJib2xkIiksDQogICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImJsYWNrIiwgZmlsbCA9IE5BLCBzaXplID0gLjUpLA0KICAgICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpDQpgYGANCg0KYGBge3IsIHdhcm5pbmc9RkFMU0V9DQpwcm9wZXJhdGlfMSAlPiUNCiAgc2VsZWN0KC1pZCwtbDMscHJvcGVydHlfdHlwZSkgJT4lIA0KDQpnZ3BhaXJzKC4sIA0KICAgICAgICB0aXRsZSA9ICJNYXRyaXogZGUgY29ycmVsYWNpb25lcyIsDQogICAgICAgIG1hcHBpbmcgPSBhZXMoY29sb3VyPSBwcm9wZXJ0eV90eXBlKSkNCmBgYA0KDQoNCiM3Lk1vZGVsbyBsaW5lYWwNCg0KUmVhbGl6YXIgdW4gbW9kZWxvIGxpbmVhbCBzaW1wbGUgcGFyYSBleHBsaWNhciBlbCBwcmVjaW8gZW4gZnVuY2nDs24gZGUgbGFzIGhhYml0YWNpb25lcyAocm9vbXMpIHkgb3RybyBtb2RlbG8gcXVlIGV4cGxpcXVlIGVsIHByZWNpbyBlbiBmdW5jacOzbiBkZSBsYSBzdXBlcmZpY2llIHRvdGFsIChzdXJmYWNlX3RvdGFsKQ0KDQpVc2FyIGxhIGZ1bmNpw7NuIHN1bW1hcnkoKSBwYXJhIG9idGVuZXIgaW5mb3JtYWNpb24gZGUgYW1ib3MgbW9kZWxvcy4gRXhwbGljYXIgbG9zIHZhbG9yZXMgZGUgbG9zIGNvZWZpY2llbnRlcyBlc3RpbWFkb3MuDQrCv0N1w6FsIG1vZGVsbyB1c2Fyw61hbiBwYXJhIHByZWRlY2lyIGVsIHByZWNpbz8gwr9Qb3IgcXXDqT8NCg0KDQojIyBNb2RlbG8gSTogcHJlY2lvKHJvb21zKQ0KDQpFbCBtb2RlbG8gSSBidXNjYSBleHBsaWNhciBsb3MgcHJlY2lvcyBjb21vIHVuYSBmdW5jacOzbiBsaW5lYWwgZGUgbGEgY2FudGlkYWQgZGUgaGFiaXRhY2lvbmVzLiBFbCBjb2VmaWNpZW50ZSBxdWUgdmluY3VsYSBlc3RhcyB2YXJpYWJsZXMgZXMgcG9zaXRpdm8geSBzaWduaWZpY2F0aXZvIGNvbiB1biBwLXZhbHVlIDwyZS0xNi4gQXNpbWlzbW8sIGVsIGNvZmllY2llbnRlICg1OC44NTcpIHB1ZWRlIGludGVycHJldGFyc2UgY29tbzogcG9yIGNhZGEgaGFiaXRhY2nDs24gZWwgcHJlY2lvIGRlIGxhIHZpdmllbmRhc2UgaW5jcmVtZW50YSBlbiA1OCBtaWwgZMOzbGFyZXMuDQoNCkVsIFIgY3VhZHJhZG8gaW5kaWNhIHF1ZSBlbCBtb2RlbG8gbG9ncmEgZXhwbGljYXIgdW4gNDAlIGRlIGxhIHZhcmlhYmlsaWRhZCBkZWwgcHJlY2lvLg0KDQpgYGB7cn0NCg0KbW9kZWxvX3Jvb21zPC1sbShkYXRhID0gcHJvcGVyYXRpXzEsZm9ybXVsYSA9IHByaWNlIH4gcm9vbXMpDQpzdW1tYXJ5KG1vZGVsb19yb29tcykNCg0KDQpnZ3Bsb3QocHJvcGVyYXRpXzEsbWFwcGluZyA9IGFlcyh4PXByaWNlLCB5ID0gcm9vbXMpKStnZW9tX3BvaW50KCkrZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIikrIGxhYnModGl0bGUgPSAiQ2FudGlkYWQgZGUgaGFiaXRhY2lvbmVzIHkgcHJlY2lvcyBkZSBpbm11ZWJsZXMiLCBzdWJ0aXRsZSA9ICIiKSt4bGFiKCJQcmVjaW8gKFVTRCkiKSt5bGFiKCJDYXQuIEhhYml0YWNpb25lcyIpK3RoZW1lX2J3KCkrDQp0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgZmFjZSA9ICJib2xkIiksDQogICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImJsYWNrIiwgZmlsbCA9IE5BLCBzaXplID0gLjUpKQ0KDQpgYGANCg0KIyMgTW9kZWxvIElJOiBwcmVjaW8oc3VmYWNlLXRvdGFsKQ0KDQpFbCBtb2RlbG8gSUkgYnVzY2EgZXhwbGljYXIgbG9zIHByZWNpb3MgY29tbyB1bmEgZnVuY2nDs24gYSBsYSBzdXBlcmZpY2llIHRvdGFsLiBMYSBwcmVzZW5jaWEgZGUgb3V0bGllcnMgaGFjZSBxdWUgbG8gY29lZmljaWVudGVzIGVzdMOpbiBtYWwgZXN0aW1hZG9zLiANCg0KcG9yIGVzdGEgcmF6w7NuIHNlIHJlZXRpbW8gZWwgbW9kZWxvIHF1aXRhbmRvIGxhcyB2aXZpZW5kYXMgY29uIG3DoXMgZGUgMTUwMCBtZXRyb3MgY3VhZHJhZG9zLg0KDQpgYGB7cn0NCm1vZGVsb19zdXJmYWNlPC1sbShkYXRhID0gcHJvcGVyYXRpXzEsIHByaWNlIH4gc3VyZmFjZV90b3RhbCkNCnN1bW1hcnkobW9kZWxvX3N1cmZhY2UpDQoNCmdncGxvdChwcm9wZXJhdGlfMSxtYXBwaW5nID0gYWVzKHg9cHJpY2UsIHkgPSBzdXJmYWNlX3RvdGFsKSkrZ2VvbV9wb2ludCgpK2dlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIpKyBsYWJzKHRpdGxlID0gIlN1cGVyZmljaWUgdG90YWwgeSBwcmVjaW9zIGRlIGlubXVlYmxlcyIsIHN1YnRpdGxlID0gIiIpK3hsYWIoIlByZWNpbyAoVVNEKSIpK3lsYWIoIlN1cGVyZmljaWUgKG0yKSIpK3RoZW1lX2J3KCkrDQp0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgZmFjZSA9ICJib2xkIiksDQogICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImJsYWNrIiwgZmlsbCA9IE5BLCBzaXplID0gLjUpKQ0KYGBgDQoNCiMjIE1vZGVsbyBJSSBzaW4gb3VsaWVyczogcHJlY2lvKHN1ZmFjZS10b3RhbCkNCg0KRWwgbW9kZWxvIGNvcnJlZ2lkbyByZWdpc3RyYSBjb2VmaWNpZW50ZXMgcG9zaXRpdm8geSBzaWduaWZpY2F0aXZvcyBhbCAxJS4gQXNpbWlzbW8sIGVsIGNvZmllY2llbnRlICgxMjY5LjYpIHF1ZSBhc29jaWEgbGEgc3VwZXJmY2llIGFsIHByZWNpbyBwdWVkZSBpbnRlcnByZXRhcnNlIGNvbW86IHBvciBjYWRhIG1ldHJvIGN1YWRyYWRvIGRlIHN1cGVyZmljaWUgZWwgcHJlY2lvIGRlIGxhIHZpdmllbmRhc2UgaW5jcmVtZW50YSBlbiAxMjcwIGTDs2xhcmVzLg0KDQpFbCBSIGN1YWRyYWRvIGluZGljYSBxdWUgZWwgbW9kZWxvIGxvZ3JhIGV4cGxpY2FyIGFwZW5hcyB1biA0OCUgZGUgbGEgdmFyaWFiaWxpZGFkIGRlbCBwcmVjaW8uDQoNCmBgYHtyfQ0KbW9kZWxvX3N1cmZhY2U8LWxtKGRhdGEgPSBwcm9wZXJhdGlfMVtwcm9wZXJhdGlfMSRzdXJmYWNlX3RvdGFsPDE1MDAsXSwgcHJpY2UgfiBzdXJmYWNlX3RvdGFsKQ0Kc3VtbWFyeShtb2RlbG9fc3VyZmFjZSkNCg0KZ2dwbG90KHByb3BlcmF0aV8xW3Byb3BlcmF0aV8xJHN1cmZhY2VfdG90YWw8MTUwMCxdLG1hcHBpbmcgPSBhZXMoeD1wcmljZSwgeSA9IHN1cmZhY2VfdG90YWwpKStnZW9tX3BvaW50KCkrZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIikrIGxhYnModGl0bGUgPSAiU3VwZXJmaWNpZSB0b3RhbCB5IHByZWNpb3MgZGUgaW5tdWVibGVzIiwgc3VidGl0bGUgPSAiIikreGxhYigiUHJlY2lvIChVU0QpIikreWxhYigiU3VwZXJmaWNpZSAobTIpIikrdGhlbWVfYncoKSsNCnRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBmYWNlID0gImJvbGQiKSwNCiAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiYmxhY2siLCBmaWxsID0gTkEsIHNpemUgPSAuNSkpDQoNCmBgYA0KDQojIyBTZWxlY2Npw7NuIGRlbCBtb2RlbG8NCg0KRGFkbyBxdWUgZWwgUiBjdWFkcmFkbyBlcyBzdXBlcmlvciBlbiBlbCBzZWd1bmRvIG1vZGVsbywgc2UgcHVlZGUgYXJndW1lbnRhciBxdWUgZXN0ZSBtb2RlbG8gbG9ncmEgZXhwbGljYXIgdW5hIG1heW9yIHByb3BvcmNpw7NuIGRlIGxhIHZhcmlhYmlsaWRhIHkgcG9yIGxvIHRhbnRvIHJlc3VsdGEgc3VwZXJpb3IgYWwgbW9kZWxvIGRlIGhhYml0YWNpb25lcy4gDQoNCg0K