Alumno: Tomás Ariel D’Amelio

mail: dameliotomas@gmail.com

Cargo librerias necesarias para la realización del presente trabajo práctico
library(tidyverse)
library(openintro)
library(GGally)
library(corrr)
library(knitr)
library(kableExtra)
library(ggplot2)
library(purrr)

1. Preparación de los datos

a. Leer el archivo ar_properties.csv y mostrar su estructura

propiedades <- read.csv(file="C:/Users/tomas/Desktop/Maestria/EEA/DiegoKoz-EEA2019-c64a28c/trabajos_practicos/TP-1/ar_properties/ar_properties.csv", header=TRUE, sep=",")
glimpse(propiedades)
Observations: 388,891
Variables: 24
$ id              <fct> S0we3z3V2JpHUJreqQ2t/w==, kMxcmAS8NvrynGBVbMOEa...
$ ad_type         <fct> Propiedad, Propiedad, Propiedad, Propiedad, Pro...
$ start_date      <fct> 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,...
$ created_on      <fct> 2019-04-14, 2019-04-14, 2019-04-14, 2019-04-14,...
$ lat             <dbl> -34.94331, -34.63181, NA, -34.65471, -34.65495,...
$ lon             <dbl> -54.92966, -58.42060, NA, -58.79089, -58.78712,...
$ l1              <fct> Uruguay, Argentina, Argentina, Argentina, Argen...
$ l2              <fct> Maldonado, Capital Federal, Bs.As. G.B.A. Zona ...
$ l3              <fct> Punta del Este, Boedo, NA, Moreno, Moreno, Rosa...
$ l4              <fct> NA, NA, NA, Moreno, Moreno, NA, Ituzaingó, NA,...
$ l5              <fct> 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,...
$ rooms           <int> 2, NA, 2, 2, 2, 4, NA, 6, NA, NA, NA, NA, NA, N...
$ bedrooms        <int> 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...
$ surface_total   <int> 45, NA, 200, 460, 660, NA, 70, NA, 1300, 405, 3...
$ surface_covered <int> 40, NA, NA, 100, 148, 89, 122, NA, NA, NA, NA, ...
$ price           <int> 13000, 0, NA, NA, NA, NA, NA, NA, 0, NA, 0, NA,...
$ currency        <fct> UYU, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA...
$ price_period    <fct> Mensual, Mensual, NA, Mensual, Mensual, Mensual...
$ title           <fct> Departamento - Roosevelt, PH - Boedo, Ituzaingo...
$ property_type   <fct> Departamento, PH, Casa, Casa, Casa, Casa, Casa,...
$ operation_type  <fct> Alquiler, Venta, Alquiler, Venta, Venta, Venta,...

b. Quedarse con aquellos registros que:

i.Pertenecen a Argentina y Capital Federal

ii. Cuyo precio esta en dolares (USD)

iii.El tipo de propiedad sea: Departamento, PH o Casa

iv.El tipo de operacion sea Venta

df <- propiedades %>% 
  filter(l1=='Argentina' , l2 =='Capital Federal')  %>%  # punto i.
  filter(currency =='USD') %>% # punto ii.
  filter(property_type == 'Departamento' | property_type == 'PH' | property_type == 'Casa' ) %>% # punto iii.
  filter(operation_type == 'Venta') # punto iv.

c. Seleccionar las variables id, l3, rooms, bedrooms, bathrooms, surface_total, surface_covered, price y property_type

df <- df %>% 
  select (id, l3, rooms, bedrooms, bathrooms, surface_total, surface_covered, price, property_type)
cat("Hata este punto del TP, el dataset contiene", dim(df)[1], "filas y", dim(df)[2], "columnas" )
Hata este punto del TP, el dataset contiene 61905 filas y 9 columnas

2. Analisis exploratorios (I)

a. Obtener la cantidad de valores únicos y de valores faltantes (NAs) para cada una de estas variables

valores_unicos <- map(df, ~n_distinct(.))
valores_faltantes <- map(df, ~sum(is.na(.)))
valores_unicos_y_faltantes <- data.frame(valores_unicos)
valores_unicos_y_faltantes <- rbind(valores_unicos_y_faltantes, valores_faltantes)
rownames(valores_unicos_y_faltantes)[1:2] <- c("unicos", "faltantes")
valores_unicos_y_faltantes %>% 
  kable() %>% 
  kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"))
id l3 rooms bedrooms bathrooms surface_total surface_covered price property_type
unicos 61905 58 24 25 15 671 573 4095 3
faltantes 0 355 5314 25298 3196 3671 2975 0 0
Puede observarse que la variable bedrooms contiene una gran cantidad de datos faltantes (25.298). Es decir que más de un 40% de los datos para esta variable son datos faltantes.
No se reportan datos faltantes para la variable precio, ni tampoco para tipo de propiedad.
Hay tres tipos de propiedad, los cuales fueron mencionados anterioremente en el TP de acuerdo a los filtros realizados. Estos son: departamento, casa y PH.

b. Obtener la matriz de correlación para las variables numéricas.

df_num <- df %>% 
  select(-id, -l3, -property_type)
df_num %>%  
  correlate(use = "complete.obs") %>% 
  shave() %>% 
  fashion()

Correlation method: 'pearson'
Missing treated using: 'complete.obs'
A partir de esta matriz es posible observar que hay una fuerte correlación entre la variable bedrooms y la variable rooms ( r =0.92).
Del mismo modo, los coeficientes de correlacion fueron mayores entre la variable precio y las variables rooms ( r =0.49), bedrooms ( r =0.43) y bathrooms ( r =0.60) que en relacion a la variable superficie de la propiedad ( r =0.05) y la superficie cubierta ( r =0.06).

3. Preparacion de los datos (II)

a. En el punto 2 deberian haber encontrado que la variable bedrooms presenta una alta proporción de valores faltantes y que presenta una fuerte correlacion con la variable rooms. Por lo tanto, vamos a eliminarla.

df <- df %>% 
  select(-bedrooms)

b. Eliminar todos los registros que presentan valores faltantes

df2 <- df[complete.cases(df), ]
cantidad_datos_con_NA <- dim(df)-dim(df2)
cat("Fueron eliminados por presentar valores faltantes", cantidad_datos_con_NA [1], "registros.\nHasta este punto del TP, el dataset contiene", dim(df2)[1], "filas y", dim(df2)[2], "columnas" )
Fueron eliminados por presentar valores faltantes 10695 registros.
Hasta este punto del TP, el dataset contiene 51210 filas y 8 columnas

4. Analisis exploratorios (II)

a. Obtener estadísticas descriptivas para la variable precio (cuartiles, promedio, minimo y maximo) y realizar un histograma de la variable

df <- df2
histograma_precio <- function (x) {
  qplot(x$price,
      geom="histogram",
      main="Distribucion de los precios de las propiedades", 
      xlab="Precios (en dolares)",
      ylab="Cantidad",
      binwidth=50000,  
      fill=I("blue"), 
      alpha=I(.2),
      ....=c(20,50))
}
histograma_precio(df)
Ignoring unknown parameters: ....

summary(df$price)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   6000  119000  170000  251577  270000 6000000 
Al observar el histograma podemos ver una clara asimetria a derecha, acorde con el tipo de distribución de precios de inmuebles que se puede observar generalmente en el mercado.
Vemos ademas que el valor minimo de la propiedad es 6000 dolares, lo cual claramente no tiene sentido y se deba propablemente a un error de carga.

b. Obtener estadísticas descriptivas para la variable precio (cuartiles, promedio, minimo y maximo) por cada tipo de propiedad.

descriptivas <- function (x) {
  x %>% 
    group_by(property_type) %>% 
    summarise(Q1     = quantile(price, 0.25),
              Mediann= quantile(price, 0.5),
              Media  = mean    (price),
              Q3     = quantile(price, 0.75),
              Minimo = min     (price),
              Maximo = max     (price))
}
descriptivas(df)
Del mismo modo que se había mostrado previamente, los valores minimos se encuentran por fuera del rango veradero del valor de propiedades.
Otro dato importante a destacar es que la mediana de los precios de las casas es de USD 335.000, lo cual es ampliamente superior a la mediana de los precios de los departamentos (USD 164.000) y de los PH (USD 190.000)

c. Realizar un grafico de boxplot de la variable precio por tipo de propiedad

boxplot_precio <- function (x) { 
ggplot(x, aes(x = property_type, y = price, group = property_type, fill = property_type )) +
  labs(title = 'Precios segun el tipo de propiedad', x = 'Tipo de propiedad', y = 'Precio', fill = 'Tipo de propiedad') +
  geom_boxplot(alpha=0.2)
}
boxplot_precio(df)

En este boxplot puede verse que la mediana es mayor para los precios de las casas en comparacion con los departamentos y los PH.
Tambien puede verse que en los tres tipos de propiedades hay gran cantidad de valores que se alejan mas alla de 1.5 IQR. Sin embargo, no se usara esa métrica para descartar outliers, como se vera luego en el punto 5 de este TP.

d. Realizar un correlagrama usando GGAlly

correlagrama <- function (x) {
  x %>%
    select(-id, -l3) %>%
    ggpairs(., 
    title = "Correlograma segun tipo de propiedad",
    mapping = aes(colour= property_type))
}
correlagrama (df)

En primer lugar, aquello que puede verse es que la mayor cantidad de datos corresponde al tipo de propiedad “departamento”. Ademas, y adicionalmente a lo antes descripto, puede verse que los valores de correlación desglosado por tipo de propiedad son mucho mayores en comparación con las correlaciones globales entre las variables numéricas que componen este set de datos.

5. Outliers

a. Eliminar los outliers de la variable precio con algún criterio que elijan: puede ser por valores de corte, eliminar el x% a izquierda y derecha,etc.

Se eligió como criterios de corte no en relación al precio absoluto, sino al valor del metro cuadrado. Así, si estableció valor mínimo del metro cuadrado 1.000 USD y como valor maximo 20.000 USD
df2 <- df %>% 
  mutate(metro_cuadrado = price/surface_total) %>% 
  filter(metro_cuadrado >= 1000, metro_cuadrado <= 20000)
cantidad_outliers <- dim(df)-dim(df2)
cat("Fueron detectados y eliminados como outliers", cantidad_outliers[1], "casos")
Fueron detectados y eliminados como outliers 860 casos

6. Analisis exploratorios (III)

a. Repetir los 4 análisis exploratorios realizados en el punto 4 y realizar unos breves comentarios sobre los cambios que encontraron

summary(df2$price)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  24000  119000  170000  251614  270000 6000000 
histograma_precio(df2)
Ignoring unknown parameters: ....

descriptivas(df2)
boxplot_precio(df2)

correlagrama(df2)

Puede observarse que hay un aumento en el valor mínimo del precio de las propiedades (antes:USD 6.000 / ahora:USD 24.000), mientras el resto de los valores se mantienen relativamente constantes.
Si si realiza el desglose por tipo de propiedad, es posible observar un cambio a nivel de los valores mínimos de todos los tipos de propiedades:
- Casa –> antes: USD 20.000 / ahora: USD 62.000
- Departamento –> antes: USD 6.000 / ahora: USD 24.000
- PH –> antes: USD 32.000 / ahora: USD 45.000
Los boxplots desglozados por tipo de propiedad se mantuvieron practicamente sin cambios, lo cual da cuenta el metodo utilizado para detectar outliers y eliminarlos (relativo al metro cuadrado) es independiente de los valores que parecen outliers a partir de los valores absolutos de precios segun tipo de propiedad, posible de ser medidos por la distancias de 1.5 IQR. Finalmente, las correlaciones dan mas altas al quitar los outliers.

7. Modelo lineal

a. 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)

Modelo1: “habitaciones”

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

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

Residuals:
     Min       1Q   Median       3Q      Max 
-2920646  -100166   -33322    42834  5387301 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) -58366.2     2519.6  -23.16   <2e-16 ***
rooms       111844.1      816.5  136.97   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 248500 on 50348 degrees of freedom
Multiple R-squared:  0.2715,    Adjusted R-squared:  0.2715 
F-statistic: 1.876e+04 on 1 and 50348 DF,  p-value: < 2.2e-16
ggplot2::ggplot(df2, ggplot2::aes(x=rooms, y=price)) +
                         ggplot2::geom_point(ggplot2::aes()) +
                         ggplot2::geom_smooth(method = "lm") +
                         ggplot2::labs(x = "Cantidad de habitaciones", 
                                       y = "Precio en dolares",
                                       title = "Modelo lineal simple de precio en funcion de habitaciones") 

Modelo2: “superficie”

modelo_superficie_total <- lm(price ~ surface_total, data = df2)
summary(modelo_superficie_total)

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

Residuals:
     Min       1Q   Median       3Q      Max 
-2199237   -43180    -6536    25067  4245174 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)    
(Intercept)   -8113.99    1361.93  -5.958 2.57e-09 ***
surface_total  2938.23      11.85 247.991  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 195400 on 50348 degrees of freedom
Multiple R-squared:  0.5499,    Adjusted R-squared:  0.5498 
F-statistic: 6.15e+04 on 1 and 50348 DF,  p-value: < 2.2e-16

b. Usar la función summary() para obtener informacion de ambos modelos. Explicar los valores de los coeficientes estimados.

A continuacion se explicaran los valores de los coeficientes estimados.

El intercepto, o β0, dio para el primer modelo -58366.2 mientras que para el segundo modelo -8113.99 El intercepto no es posible ser interpretado, ya que no tiene sentido interpretar el valor que tomaria (según esta predicción)una propiedad en caso que tenga 0 cuartos, o si tuviera 0 metros cuadrados de superficie.

Aquello que si se puede interpretar es el β1, que para el modelo 1 significa cuanto aumentaría (según la prediccion de este modelo) el valor de la propiedad por cada cuarto extra que tiene (por cada cuarto extra el valor de la propiedad aumenta, segun este modelo, en USD 111844.1 ); y para el modelo 2 significa cuanto aumenta este valor por cada metro cuadrado extra de la misma (por cada metro cuadrado extra el valor de la propiedad aumenta, segun este modelo, en USD 2938.23)

c. ¿Cuál modelo usarían para predecir el precio? ¿Por qué?

Finalmente, utilizaria el modelo que toma como valor de entrada la superficie de la propiedad (modelo 2), teniendo en cuenta que el R-cuadrado es mayor (R2= 0.55)al modelo que toma como valor de entrada la cantidad de cuartos(R2=0.27).

LS0tDQp0aXRsZTogIlRQMTogQW7DoWxpc2lzIEV4cGxvcmF0b3JpbyBlIEludHJvZHVjY2nDs24gYSBSZWdyZXNpw7NuIGxpbmVhbCINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCiMjICoqQWx1bW5vOioqIFRvbcOhcyBBcmllbCBEJ0FtZWxpbw0KIyMjIG1haWw6IGRhbWVsaW90b21hc0BnbWFpbC5jb20gDQoNCiMjIyMjIENhcmdvIGxpYnJlcmlhcyBuZWNlc2FyaWFzIHBhcmEgbGEgcmVhbGl6YWNpw7NuIGRlbCBwcmVzZW50ZSB0cmFiYWpvIHByw6FjdGljbw0KDQpgYGB7cn0NCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShvcGVuaW50cm8pDQpsaWJyYXJ5KEdHYWxseSkNCmxpYnJhcnkoY29ycnIpDQpsaWJyYXJ5KGtuaXRyKQ0KbGlicmFyeShrYWJsZUV4dHJhKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShwdXJycikNCmBgYA0KDQoNCiMjICoqMS4gUHJlcGFyYWNpw7NuIGRlIGxvcyBkYXRvcyoqICANCiMjIyAgICAgICBhLiBMZWVyIGVsIGFyY2hpdm8gYXJfcHJvcGVydGllcy5jc3YgeSBtb3N0cmFyIHN1IGVzdHJ1Y3R1cmENCg0KYGBge3J9DQoNCnByb3BpZWRhZGVzIDwtIHJlYWQuY3N2KGZpbGU9IkM6L1VzZXJzL3RvbWFzL0Rlc2t0b3AvTWFlc3RyaWEvRUVBL0RpZWdvS296LUVFQTIwMTktYzY0YTI4Yy90cmFiYWpvc19wcmFjdGljb3MvVFAtMS9hcl9wcm9wZXJ0aWVzL2FyX3Byb3BlcnRpZXMuY3N2IiwgaGVhZGVyPVRSVUUsIHNlcD0iLCIpDQpnbGltcHNlKHByb3BpZWRhZGVzKQ0KYGBgDQoNCiMjIyBiLiBRdWVkYXJzZSBjb24gYXF1ZWxsb3MgcmVnaXN0cm9zIHF1ZTogICAgDQojIyMjICAgICAgICAgIGkuUGVydGVuZWNlbiBhIEFyZ2VudGluYSB5IENhcGl0YWwgRmVkZXJhbCAgDQojIyMjICAgICAgICAgaWkuIEN1eW8gcHJlY2lvIGVzdGEgZW4gZG9sYXJlcyAoVVNEKSAgDQojIyMjICAgICAgICAgaWlpLkVsIHRpcG8gZGUgcHJvcGllZGFkIHNlYTogRGVwYXJ0YW1lbnRvLCBQSCBvIENhc2EgIA0KIyMjIyAgICAgICAgIGl2LkVsIHRpcG8gZGUgb3BlcmFjaW9uIHNlYSBWZW50YSAgDQoNCmBgYHtyfQ0KZGYgPC0gcHJvcGllZGFkZXMgJT4lIA0KICBmaWx0ZXIobDE9PSdBcmdlbnRpbmEnICwgbDIgPT0nQ2FwaXRhbCBGZWRlcmFsJykgICU+JSAgIyBwdW50byBpLg0KICBmaWx0ZXIoY3VycmVuY3kgPT0nVVNEJykgJT4lICMgcHVudG8gaWkuDQogIGZpbHRlcihwcm9wZXJ0eV90eXBlID09ICdEZXBhcnRhbWVudG8nIHwgcHJvcGVydHlfdHlwZSA9PSAnUEgnIHwgcHJvcGVydHlfdHlwZSA9PSAnQ2FzYScgKSAlPiUgIyBwdW50byBpaWkuDQogIGZpbHRlcihvcGVyYXRpb25fdHlwZSA9PSAnVmVudGEnKSAjIHB1bnRvIGl2Lg0KDQpgYGANCg0KIyMjIGMuIFNlbGVjY2lvbmFyIGxhcyB2YXJpYWJsZXMgaWQsIGwzLCByb29tcywgYmVkcm9vbXMsIGJhdGhyb29tcywgc3VyZmFjZV90b3RhbCwgc3VyZmFjZV9jb3ZlcmVkLCBwcmljZSB5IHByb3BlcnR5X3R5cGUNCiAgDQpgYGB7cn0NCmRmIDwtIGRmICU+JSANCiAgc2VsZWN0IChpZCwgbDMsIHJvb21zLCBiZWRyb29tcywgYmF0aHJvb21zLCBzdXJmYWNlX3RvdGFsLCBzdXJmYWNlX2NvdmVyZWQsIHByaWNlLCBwcm9wZXJ0eV90eXBlKQ0KY2F0KCJIYXRhIGVzdGUgcHVudG8gZGVsIFRQLCBlbCBkYXRhc2V0IGNvbnRpZW5lIiwgZGltKGRmKVsxXSwgImZpbGFzIHkiLCBkaW0oZGYpWzJdLCAiY29sdW1uYXMiICkNCmBgYA0KDQojIyAgKioyLiBBbmFsaXNpcyBleHBsb3JhdG9yaW9zIChJKSoqICANCiMjIyAgICAgIGEuIE9idGVuZXIgbGEgY2FudGlkYWQgZGUgdmFsb3JlcyDDum5pY29zIHkgZGUgdmFsb3JlcyBmYWx0YW50ZXMgKE5BcykgcGFyYSBjYWRhIHVuYSBkZSBlc3RhcyB2YXJpYWJsZXMgIA0KDQpgYGB7cn0NCnZhbG9yZXNfdW5pY29zIDwtIG1hcChkZiwgfm5fZGlzdGluY3QoLikpDQp2YWxvcmVzX2ZhbHRhbnRlcyA8LSBtYXAoZGYsIH5zdW0oaXMubmEoLikpKQ0KdmFsb3Jlc191bmljb3NfeV9mYWx0YW50ZXMgPC0gZGF0YS5mcmFtZSh2YWxvcmVzX3VuaWNvcykNCnZhbG9yZXNfdW5pY29zX3lfZmFsdGFudGVzIDwtIHJiaW5kKHZhbG9yZXNfdW5pY29zX3lfZmFsdGFudGVzLCB2YWxvcmVzX2ZhbHRhbnRlcykNCnJvd25hbWVzKHZhbG9yZXNfdW5pY29zX3lfZmFsdGFudGVzKVsxOjJdIDwtIGMoInVuaWNvcyIsICJmYWx0YW50ZXMiKQ0KdmFsb3Jlc191bmljb3NfeV9mYWx0YW50ZXMgJT4lIA0KICBrYWJsZSgpICU+JSANCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiLCAiY29uZGVuc2VkIiwgInJlc3BvbnNpdmUiKSkNCmBgYA0KIyMjIyMgUHVlZGUgb2JzZXJ2YXJzZSBxdWUgbGEgdmFyaWFibGUgX2JlZHJvb21zXyBjb250aWVuZSB1bmEgZ3JhbiBjYW50aWRhZCBkZSBkYXRvcyBmYWx0YW50ZXMgKDI1LjI5OCkuIEVzIGRlY2lyIHF1ZSBtw6FzIGRlIHVuIDQwJSBkZSBsb3MgZGF0b3MgcGFyYSBlc3RhIHZhcmlhYmxlIHNvbiBkYXRvcyBmYWx0YW50ZXMuIA0KIyMjIyMgTm8gc2UgcmVwb3J0YW4gZGF0b3MgZmFsdGFudGVzIHBhcmEgbGEgdmFyaWFibGUgX3ByZWNpb18sIG5pIHRhbXBvY28gcGFyYSAgX3RpcG8gZGUgcHJvcGllZGFkXy4NCiMjIyMjIEhheSB0cmVzIHRpcG9zIGRlIHByb3BpZWRhZCwgbG9zIGN1YWxlcyBmdWVyb24gbWVuY2lvbmFkb3MgYW50ZXJpb3JlbWVudGUgZW4gZWwgVFAgZGUgYWN1ZXJkbyBhIGxvcyBmaWx0cm9zIHJlYWxpemFkb3MuIEVzdG9zIHNvbjogZGVwYXJ0YW1lbnRvLCBjYXNhIHkgUEguDQoNCiMjIyBiLiBPYnRlbmVyIGxhIG1hdHJpeiBkZSBjb3JyZWxhY2nDs24gcGFyYSBsYXMgdmFyaWFibGVzIG51bcOpcmljYXMuDQogIA0KYGBge3J9DQpkZl9udW0gPC0gZGYgJT4lIA0KICBzZWxlY3QoLWlkLCAtbDMsIC1wcm9wZXJ0eV90eXBlKQ0KZGZfbnVtICU+JSAgDQogIGNvcnJlbGF0ZSh1c2UgPSAiY29tcGxldGUub2JzIikgJT4lIA0KICBzaGF2ZSgpICU+JSANCiAgZmFzaGlvbigpDQpgYGANCiMjIyMjIEEgcGFydGlyIGRlIGVzdGEgbWF0cml6IGVzIHBvc2libGUgb2JzZXJ2YXIgcXVlIGhheSB1bmEgZnVlcnRlIGNvcnJlbGFjacOzbiBlbnRyZSBsYSB2YXJpYWJsZSBfYmVkcm9vbXNfIHkgbGEgdmFyaWFibGUgX3Jvb21zXyAoIF9yXyA9MC45MikuDQojIyMjIyBEZWwgbWlzbW8gbW9kbywgbG9zIGNvZWZpY2llbnRlcyBkZSBjb3JyZWxhY2lvbiBmdWVyb24gbWF5b3JlcyBlbnRyZSBsYSB2YXJpYWJsZSBfcHJlY2lvXyB5IGxhcyB2YXJpYWJsZXMgX3Jvb21zXyAoIF9yXyA9MC40OSksIF9iZWRyb29tc18gKCBfcl8gPTAuNDMpIHkgX2JhdGhyb29tc18gKCBfcl8gPTAuNjApIHF1ZSBlbiByZWxhY2lvbiBhIGxhIHZhcmlhYmxlIF9zdXBlcmZpY2llIGRlIGxhIHByb3BpZWRhZF8gKCBfcl8gPTAuMDUpIHkgbGEgX3N1cGVyZmljaWUgY3ViaWVydGFfICggX3JfID0wLjA2KS4gDQoNCg0KIyMgICoqMy4gUHJlcGFyYWNpb24gZGUgbG9zIGRhdG9zIChJSSkqKiAgDQojIyMgICAgICAgIGEuIEVuIGVsIHB1bnRvIDIgZGViZXJpYW4gaGFiZXIgZW5jb250cmFkbyBxdWUgbGEgdmFyaWFibGUgYmVkcm9vbXMgcHJlc2VudGEgdW5hIGFsdGEgcHJvcG9yY2nDs24gZGUgdmFsb3JlcyBmYWx0YW50ZXMgeSBxdWUgcHJlc2VudGEgdW5hIGZ1ZXJ0ZSBjb3JyZWxhY2lvbiBjb24gbGEgdmFyaWFibGUgcm9vbXMuIFBvciBsbyB0YW50bywgdmFtb3MgYSBlbGltaW5hcmxhLg0KICAgIA0KYGBge3J9DQpkZiA8LSBkZiAlPiUgDQogIHNlbGVjdCgtYmVkcm9vbXMpDQpgYGANCg0KIyMjIGIuIEVsaW1pbmFyIHRvZG9zIGxvcyByZWdpc3Ryb3MgcXVlIHByZXNlbnRhbiB2YWxvcmVzIGZhbHRhbnRlcw0KICAgIA0KYGBge3J9DQpkZjIgPC0gZGZbY29tcGxldGUuY2FzZXMoZGYpLCBdDQpjYW50aWRhZF9kYXRvc19jb25fTkEgPC0gZGltKGRmKS1kaW0oZGYyKQ0KY2F0KCJGdWVyb24gZWxpbWluYWRvcyBwb3IgcHJlc2VudGFyIHZhbG9yZXMgZmFsdGFudGVzIiwgY2FudGlkYWRfZGF0b3NfY29uX05BIFsxXSwgInJlZ2lzdHJvcy5cbkhhc3RhIGVzdGUgcHVudG8gZGVsIFRQLCBlbCBkYXRhc2V0IGNvbnRpZW5lIiwgZGltKGRmMilbMV0sICJmaWxhcyB5IiwgZGltKGRmMilbMl0sICJjb2x1bW5hcyIgKQ0KYGBgDQoNCiMjICAqKjQuIEFuYWxpc2lzIGV4cGxvcmF0b3Jpb3MgKElJKSoqICANCiMjIyAgICAgICAgYS4gT2J0ZW5lciBlc3RhZMOtc3RpY2FzIGRlc2NyaXB0aXZhcyBwYXJhIGxhIHZhcmlhYmxlIHByZWNpbyAoY3VhcnRpbGVzLCBwcm9tZWRpbywgbWluaW1vIHkgbWF4aW1vKSB5IHJlYWxpemFyIHVuIGhpc3RvZ3JhbWEgZGUgbGEgdmFyaWFibGUNCiAgICANCmBgYHtyfQ0KZGYgPC0gZGYyDQoNCmhpc3RvZ3JhbWFfcHJlY2lvIDwtIGZ1bmN0aW9uICh4KSB7DQogIHFwbG90KHgkcHJpY2UsDQogICAgICBnZW9tPSJoaXN0b2dyYW0iLA0KICAgICAgbWFpbj0iRGlzdHJpYnVjaW9uIGRlIGxvcyBwcmVjaW9zIGRlIGxhcyBwcm9waWVkYWRlcyIsIA0KICAgICAgeGxhYj0iUHJlY2lvcyAoZW4gZG9sYXJlcykiLA0KICAgICAgeWxhYj0iQ2FudGlkYWQiLA0KICAgICAgYmlud2lkdGg9NTAwMDAsICANCiAgICAgIGZpbGw9SSgiYmx1ZSIpLCANCiAgICAgIGFscGhhPUkoLjIpLA0KICAgICAgLi4uLj1jKDIwLDUwKSkNCn0NCmhpc3RvZ3JhbWFfcHJlY2lvKGRmKQ0KYGBgDQoNCmBgYHtyfQ0Kc3VtbWFyeShkZiRwcmljZSkNCmBgYA0KIyMjIyMgQWwgb2JzZXJ2YXIgZWwgaGlzdG9ncmFtYSBwb2RlbW9zIHZlciB1bmEgY2xhcmEgYXNpbWV0cmlhIGEgZGVyZWNoYSwgYWNvcmRlIGNvbiBlbCB0aXBvIGRlIGRpc3RyaWJ1Y2nDs24gZGUgcHJlY2lvcyBkZSBpbm11ZWJsZXMgcXVlIHNlIHB1ZWRlIG9ic2VydmFyIGdlbmVyYWxtZW50ZSBlbiBlbCBtZXJjYWRvLg0KIyMjIyMgVmVtb3MgYWRlbWFzIHF1ZSBlbCB2YWxvciBtaW5pbW8gZGUgbGEgcHJvcGllZGFkIGVzIDYwMDAgZG9sYXJlcywgbG8gY3VhbCBjbGFyYW1lbnRlIG5vIHRpZW5lIHNlbnRpZG8geSBzZSBkZWJhIHByb3BhYmxlbWVudGUgYSB1biBlcnJvciBkZSBjYXJnYS4gDQoNCiMjIyBiLiBPYnRlbmVyIGVzdGFkw61zdGljYXMgZGVzY3JpcHRpdmFzIHBhcmEgbGEgdmFyaWFibGUgcHJlY2lvIChjdWFydGlsZXMsIHByb21lZGlvLCBtaW5pbW8geSBtYXhpbW8pIHBvciBjYWRhIHRpcG8gZGUgcHJvcGllZGFkLg0KYGBge3J9DQpkZXNjcmlwdGl2YXMgPC0gZnVuY3Rpb24gKHgpIHsNCiAgeCAlPiUgDQogICAgZ3JvdXBfYnkocHJvcGVydHlfdHlwZSkgJT4lIA0KICAgIHN1bW1hcmlzZShRMSAgICAgPSBxdWFudGlsZShwcmljZSwgMC4yNSksDQogICAgICAgICAgICAgIE1lZGlhbm49IHF1YW50aWxlKHByaWNlLCAwLjUpLA0KICAgICAgICAgICAgICBNZWRpYSAgPSBtZWFuICAgIChwcmljZSksDQogICAgICAgICAgICAgIFEzICAgICA9IHF1YW50aWxlKHByaWNlLCAwLjc1KSwNCiAgICAgICAgICAgICAgTWluaW1vID0gbWluICAgICAocHJpY2UpLA0KICAgICAgICAgICAgICBNYXhpbW8gPSBtYXggICAgIChwcmljZSkpDQp9DQpkZXNjcmlwdGl2YXMoZGYpDQoNCmBgYA0KIyMjIyMgRGVsIG1pc21vIG1vZG8gcXVlIHNlIGhhYsOtYSBtb3N0cmFkbyBwcmV2aWFtZW50ZSwgbG9zIHZhbG9yZXMgbWluaW1vcyBzZSBlbmN1ZW50cmFuIHBvciBmdWVyYSBkZWwgcmFuZ28gdmVyYWRlcm8gZGVsIHZhbG9yIGRlIHByb3BpZWRhZGVzLiANCiMjIyMjIE90cm8gZGF0byBpbXBvcnRhbnRlIGEgZGVzdGFjYXIgZXMgcXVlIGxhIG1lZGlhbmEgZGUgbG9zIHByZWNpb3MgZGUgbGFzIGNhc2FzIGVzIGRlICBVU0QgMzM1LjAwMCwgbG8gY3VhbCBlcyBhbXBsaWFtZW50ZSBzdXBlcmlvciBhIGxhIG1lZGlhbmEgZGUgbG9zIHByZWNpb3MgZGUgbG9zIGRlcGFydGFtZW50b3MgKFVTRCAxNjQuMDAwKSB5IGRlIGxvcyBQSCAoVVNEIDE5MC4wMDApDQoNCiMjIyBjLiBSZWFsaXphciB1biBncmFmaWNvIGRlIGJveHBsb3QgZGUgbGEgdmFyaWFibGUgcHJlY2lvIHBvciB0aXBvIGRlIHByb3BpZWRhZA0KDQpgYGB7cn0NCmJveHBsb3RfcHJlY2lvIDwtIGZ1bmN0aW9uICh4KSB7IA0KZ2dwbG90KHgsIGFlcyh4ID0gcHJvcGVydHlfdHlwZSwgeSA9IHByaWNlLCBncm91cCA9IHByb3BlcnR5X3R5cGUsIGZpbGwgPSBwcm9wZXJ0eV90eXBlICkpICsNCiAgbGFicyh0aXRsZSA9ICdQcmVjaW9zIHNlZ3VuIGVsIHRpcG8gZGUgcHJvcGllZGFkJywgeCA9ICdUaXBvIGRlIHByb3BpZWRhZCcsIHkgPSAnUHJlY2lvJywgZmlsbCA9ICdUaXBvIGRlIHByb3BpZWRhZCcpICsNCiAgZ2VvbV9ib3hwbG90KGFscGhhPTAuMikNCn0NCmJveHBsb3RfcHJlY2lvKGRmKQ0KYGBgDQojIyMjIyBFbiBlc3RlIGJveHBsb3QgcHVlZGUgdmVyc2UgcXVlIGxhIG1lZGlhbmEgZXMgbWF5b3IgcGFyYSBsb3MgcHJlY2lvcyBkZSBsYXMgY2FzYXMgZW4gY29tcGFyYWNpb24gY29uIGxvcyBkZXBhcnRhbWVudG9zIHkgbG9zIFBILiANCiMjIyMjIFRhbWJpZW4gcHVlZGUgdmVyc2UgcXVlIGVuIGxvcyB0cmVzIHRpcG9zIGRlIHByb3BpZWRhZGVzIGhheSBncmFuIGNhbnRpZGFkIGRlIHZhbG9yZXMgcXVlIHNlIGFsZWphbiBtYXMgYWxsYSBkZSAxLjUgSVFSLiBTaW4gZW1iYXJnbywgbm8gc2UgdXNhcmEgZXNhIG3DqXRyaWNhIHBhcmEgZGVzY2FydGFyIF9vdXRsaWVyc18sIGNvbW8gc2UgdmVyYSBsdWVnbyBlbiBlbCBwdW50byA1IGRlIGVzdGUgVFAuDQoNCiMjIyBkLiBSZWFsaXphciB1biBjb3JyZWxhZ3JhbWEgdXNhbmRvIEdHQWxseQ0KICANCmBgYHtyfQ0KY29ycmVsYWdyYW1hIDwtIGZ1bmN0aW9uICh4KSB7DQogIHggJT4lDQogICAgc2VsZWN0KC1pZCwgLWwzKSAlPiUNCiAgICBnZ3BhaXJzKC4sIA0KICAgIHRpdGxlID0gIkNvcnJlbG9ncmFtYSBzZWd1biB0aXBvIGRlIHByb3BpZWRhZCIsDQogICAgbWFwcGluZyA9IGFlcyhjb2xvdXI9IHByb3BlcnR5X3R5cGUpKQ0KfQ0KY29ycmVsYWdyYW1hIChkZikNCmBgYA0KIyMjIyMgRW4gcHJpbWVyIGx1Z2FyLCBhcXVlbGxvIHF1ZSBwdWVkZSB2ZXJzZSBlcyBxdWUgbGEgbWF5b3IgY2FudGlkYWQgZGUgZGF0b3MgY29ycmVzcG9uZGUgYWwgdGlwbyBkZSBwcm9waWVkYWQgImRlcGFydGFtZW50byIuIEFkZW1hcywgeSBhZGljaW9uYWxtZW50ZSBhIGxvIGFudGVzIGRlc2NyaXB0bywgcHVlZGUgdmVyc2UgcXVlIGxvcyB2YWxvcmVzIGRlIGNvcnJlbGFjacOzbiBkZXNnbG9zYWRvIHBvciB0aXBvIGRlIHByb3BpZWRhZCBzb24gbXVjaG8gbWF5b3JlcyBlbiBjb21wYXJhY2nDs24gY29uIGxhcyBjb3JyZWxhY2lvbmVzIGdsb2JhbGVzIGVudHJlIGxhcyB2YXJpYWJsZXMgbnVtw6lyaWNhcyBxdWUgY29tcG9uZW4gZXN0ZSBzZXQgZGUgZGF0b3MuDQoNCg0KIyMgICoqNS4gT3V0bGllcnMqKiAgDQojIyMgICAgICAgIGEuIEVsaW1pbmFyIGxvcyBvdXRsaWVycyBkZSBsYSB2YXJpYWJsZSBwcmVjaW8gY29uIGFsZ8O6biBjcml0ZXJpbyBxdWUgZWxpamFuOiBwdWVkZSBzZXIgcG9yIHZhbG9yZXMgZGUgY29ydGUsIGVsaW1pbmFyIGVsIHglIGEgaXpxdWllcmRhIHkgZGVyZWNoYSxldGMuIA0KDQojIyMjIyBTZSBlbGlnacOzIGNvbW8gY3JpdGVyaW9zIGRlIGNvcnRlIG5vIGVuIHJlbGFjacOzbiBhbCBwcmVjaW8gYWJzb2x1dG8sIHNpbm8gYWwgdmFsb3IgZGVsIG1ldHJvIGN1YWRyYWRvLiBBc8OtLCBzaSBlc3RhYmxlY2nDsyB2YWxvciBtw61uaW1vIGRlbCBtZXRybyBjdWFkcmFkbyAxLjAwMCBVU0QgeSBjb21vIHZhbG9yIG1heGltbyAyMC4wMDAgVVNEDQogICAgDQpgYGB7cn0NCmRmMiA8LSBkZiAlPiUgDQogIG11dGF0ZShtZXRyb19jdWFkcmFkbyA9IHByaWNlL3N1cmZhY2VfdG90YWwpICU+JSANCiAgZmlsdGVyKG1ldHJvX2N1YWRyYWRvID49IDEwMDAsIG1ldHJvX2N1YWRyYWRvIDw9IDIwMDAwKQ0KY2FudGlkYWRfb3V0bGllcnMgPC0gZGltKGRmKS1kaW0oZGYyKQ0KY2F0KCJGdWVyb24gZGV0ZWN0YWRvcyB5IGVsaW1pbmFkb3MgY29tbyBvdXRsaWVycyIsIGNhbnRpZGFkX291dGxpZXJzWzFdLCAiY2Fzb3MiKQ0KYGBgDQoNCiMjICAqKjYuIEFuYWxpc2lzIGV4cGxvcmF0b3Jpb3MgKElJSSkqKiAgDQojIyMgICAgICAgIGEuIFJlcGV0aXIgbG9zIDQgYW7DoWxpc2lzIGV4cGxvcmF0b3Jpb3MgcmVhbGl6YWRvcyBlbiBlbCBwdW50byA0IHkgcmVhbGl6YXIgdW5vcyBicmV2ZXMgY29tZW50YXJpb3Mgc29icmUgbG9zIGNhbWJpb3MgcXVlIGVuY29udHJhcm9uDQoNCmBgYHtyfQ0Kc3VtbWFyeShkZjIkcHJpY2UpDQpoaXN0b2dyYW1hX3ByZWNpbyhkZjIpDQpkZXNjcmlwdGl2YXMoZGYyKQ0KYm94cGxvdF9wcmVjaW8oZGYyKQ0KY29ycmVsYWdyYW1hKGRmMikNCg0KYGBgDQoNClB1ZWRlIG9ic2VydmFyc2UgcXVlIGhheSB1biBhdW1lbnRvIGVuIGVsIHZhbG9yIG3DrW5pbW8gZGVsIHByZWNpbyBkZSBsYXMgcHJvcGllZGFkZXMgKGFudGVzOlVTRCA2LjAwMCAvIGFob3JhOlVTRCAyNC4wMDApLCBtaWVudHJhcyBlbCByZXN0byBkZSBsb3MgdmFsb3JlcyBzZSBtYW50aWVuZW4gcmVsYXRpdmFtZW50ZSBjb25zdGFudGVzLiAgDQpTaSBzaSByZWFsaXphIGVsIGRlc2dsb3NlIHBvciB0aXBvIGRlIHByb3BpZWRhZCwgZXMgcG9zaWJsZSBvYnNlcnZhciB1biBjYW1iaW8gYSBuaXZlbCBkZSBsb3MgdmFsb3JlcyBtw61uaW1vcyBkZSB0b2RvcyBsb3MgdGlwb3MgZGUgcHJvcGllZGFkZXM6ICANCiAtIENhc2EgICAgICAgICAtLT4gYW50ZXM6IFVTRCAyMC4wMDAgLyBhaG9yYTogVVNEIDYyLjAwMCAgDQogLSBEZXBhcnRhbWVudG8gLS0+IGFudGVzOiBVU0QgNi4wMDAgIC8gYWhvcmE6IFVTRCAyNC4wMDAgIA0KIC0gUEggICAgICAgICAgIC0tPiBhbnRlczogVVNEIDMyLjAwMCAvIGFob3JhOiBVU0QgNDUuMDAwICANCkxvcyBfYm94cGxvdHNfIGRlc2dsb3phZG9zIHBvciB0aXBvIGRlIHByb3BpZWRhZCBzZSBtYW50dXZpZXJvbiBwcmFjdGljYW1lbnRlIHNpbiBjYW1iaW9zLCBsbyBjdWFsIGRhIGN1ZW50YSBlbCBtZXRvZG8gdXRpbGl6YWRvIHBhcmEgZGV0ZWN0YXIgb3V0bGllcnMgeSBlbGltaW5hcmxvcyAocmVsYXRpdm8gYWwgbWV0cm8gY3VhZHJhZG8pIGVzIGluZGVwZW5kaWVudGUgZGUgbG9zIHZhbG9yZXMgcXVlIHBhcmVjZW4gb3V0bGllcnMgYSBwYXJ0aXIgZGUgbG9zIHZhbG9yZXMgYWJzb2x1dG9zIGRlIHByZWNpb3Mgc2VndW4gdGlwbyBkZSBwcm9waWVkYWQsIHBvc2libGUgZGUgc2VyIG1lZGlkb3MgcG9yIGxhIGRpc3RhbmNpYXMgZGUgMS41IElRUi4gDQpGaW5hbG1lbnRlLCBsYXMgY29ycmVsYWNpb25lcyBkYW4gbWFzIGFsdGFzIGFsIHF1aXRhciBsb3Mgb3V0bGllcnMuDQoNCg0KIyMgICAqKjcuICBNb2RlbG8gbGluZWFsKiogIA0KIyMjICAgICAgYS4gUmVhbGl6YXIgdW4gbW9kZWxvIGxpbmVhbCBzaW1wbGUgcGFyYSBleHBsaWNhciBlbCBwcmVjaW8gZW4gZnVuY2nDs24gZGUgbGFzIGhhYml0YWNpb25lcyAocm9vbXMpIHkgb3RybyBtb2RlbG8gcXVlIGV4cGxpcXVlIGVsIHByZWNpbyBlbiBmdW5jacOzbiBkZSBsYSBzdXBlcmZpY2llIHRvdGFsIChzdXJmYWNlX3RvdGFsKQ0KICAgICAgDQojIyMgX01vZGVsbzE6ICJoYWJpdGFjaW9uZXMiXw0KYGBge3J9DQptb2RlbG9fcm9vbXMgPC0gbG0ocHJpY2UgfiByb29tcywgZGF0YSA9IGRmMikNCnN1bW1hcnkobW9kZWxvX3Jvb21zKQ0KYGBgDQpgYGB7cn0NCmdncGxvdDI6OmdncGxvdChkZjIsIGdncGxvdDI6OmFlcyh4PXJvb21zLCB5PXByaWNlKSkgKw0KICAgICAgICAgICAgICAgICAgICAgICAgIGdncGxvdDI6Omdlb21fcG9pbnQoZ2dwbG90Mjo6YWVzKCkpICsNCiAgICAgICAgICAgICAgICAgICAgICAgICBnZ3Bsb3QyOjpnZW9tX3Ntb290aChtZXRob2QgPSAibG0iKSArDQogICAgICAgICAgICAgICAgICAgICAgICAgZ2dwbG90Mjo6bGFicyh4ID0gIkNhbnRpZGFkIGRlIGhhYml0YWNpb25lcyIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9ICJQcmVjaW8gZW4gZG9sYXJlcyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aXRsZSA9ICJNb2RlbG8gbGluZWFsIHNpbXBsZSBkZSBwcmVjaW8gZW4gZnVuY2nDs24gZGUgaGFiaXRhY2lvbmVzIikgDQpgYGANCg0KIyMjIF9Nb2RlbG8yOiAic3VwZXJmaWNpZSJfDQpgYGB7cn0NCm1vZGVsb19zdXBlcmZpY2llX3RvdGFsIDwtIGxtKHByaWNlIH4gc3VyZmFjZV90b3RhbCwgZGF0YSA9IGRmMikNCnN1bW1hcnkobW9kZWxvX3N1cGVyZmljaWVfdG90YWwpDQpgYGANCmBgYHtyfQ0KZ2dwbG90Mjo6Z2dwbG90KGRmMiwgZ2dwbG90Mjo6YWVzKHg9c3VyZmFjZV90b3RhbCwgeT1wcmljZSkpICsNCiAgICAgICAgICAgICAgICAgICAgICAgICBnZ3Bsb3QyOjpnZW9tX3BvaW50KGdncGxvdDI6OmFlcygpKSArDQogICAgICAgICAgICAgICAgICAgICAgICAgZ2dwbG90Mjo6Z2VvbV9zbW9vdGgobWV0aG9kID0gImxtIikgKw0KICAgICAgICAgICAgICAgICAgICAgICAgIGdncGxvdDI6OmxhYnMoeCA9ICJtMiBkZSBzdXBlcmZpY2llIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gIlByZWNpbyBlbiBkw7NsYXJlcyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aXRsZSA9ICJNb2RlbG8gbGluZWFsIHNpbXBsZSBkZSBwcmVjaW8gZW4gZnVuY2nDs24gZGUgbGEgc3VwZXJmaWNpZSIpIA0KYGBgDQoNCiMjIyBiLiBVc2FyIGxhIGZ1bmNpw7NuIHN1bW1hcnkoKSBwYXJhIG9idGVuZXIgaW5mb3JtYWNpb24gZGUgYW1ib3MgbW9kZWxvcy4gRXhwbGljYXIgbG9zIHZhbG9yZXMgZGUgbG9zIGNvZWZpY2llbnRlcyBlc3RpbWFkb3MuDQpBIGNvbnRpbnVhY2lvbiBzZSBleHBsaWNhcmFuIGxvcyB2YWxvcmVzIGRlIGxvcyBjb2VmaWNpZW50ZXMgZXN0aW1hZG9zLg0KDQpFbCBpbnRlcmNlcHRvLCBvIM6yMCwgZGlvIHBhcmEgZWwgcHJpbWVyIG1vZGVsbyAtNTgzNjYuMiBtaWVudHJhcyBxdWUgcGFyYSBlbCBzZWd1bmRvIG1vZGVsbyAtODExMy45OSANCkVsIGludGVyY2VwdG8gbm8gZXMgcG9zaWJsZSBzZXIgaW50ZXJwcmV0YWRvLCB5YSBxdWUgbm8gdGllbmUgc2VudGlkbyBpbnRlcnByZXRhciBlbCB2YWxvciBxdWUgdG9tYXJpYSAoc2Vnw7puIGVzdGEgcHJlZGljY2nDs24pdW5hICBwcm9waWVkYWQgZW4gY2FzbyBxdWUgdGVuZ2EgMCBjdWFydG9zLCBvIHNpIHR1dmllcmEgMCBtZXRyb3MgY3VhZHJhZG9zIGRlIHN1cGVyZmljaWUuDQoNCkFxdWVsbG8gcXVlIHNpIHNlIHB1ZWRlIGludGVycHJldGFyIGVzIGVsIM6yMSwgcXVlIHBhcmEgZWwgbW9kZWxvIDEgc2lnbmlmaWNhIGN1YW50byBhdW1lbnRhcsOtYSAoc2Vnw7puIGxhIHByZWRpY2Npb24gZGUgZXN0ZSBtb2RlbG8pIGVsIHZhbG9yIGRlIGxhIHByb3BpZWRhZCBwb3IgY2FkYSBjdWFydG8gZXh0cmEgcXVlIHRpZW5lIChwb3IgY2FkYSBjdWFydG8gZXh0cmEgZWwgdmFsb3IgZGUgbGEgcHJvcGllZGFkIGF1bWVudGEsIHNlZ3VuIGVzdGUgbW9kZWxvLCBlbiBVU0QgMTExODQ0LjEgKTsgeSBwYXJhIGVsIG1vZGVsbyAyIHNpZ25pZmljYSBjdWFudG8gYXVtZW50YSBlc3RlIHZhbG9yIHBvciBjYWRhIG1ldHJvIGN1YWRyYWRvIGV4dHJhIGRlIGxhIG1pc21hIChwb3IgY2FkYSBtZXRybyBjdWFkcmFkbyBleHRyYSBlbCB2YWxvciBkZSBsYSBwcm9waWVkYWQgYXVtZW50YSwgc2VndW4gZXN0ZSBtb2RlbG8sIGVuIFVTRCAyOTM4LjIzKQ0KDQojIyMgYy4gwr9DdcOhbCBtb2RlbG8gdXNhcsOtYW4gcGFyYSBwcmVkZWNpciBlbCBwcmVjaW8/IMK/UG9yIHF1w6k/DQpGaW5hbG1lbnRlLCB1dGlsaXphcmlhIGVsIG1vZGVsbyBxdWUgdG9tYSBjb21vIHZhbG9yIGRlIGVudHJhZGEgbGEgc3VwZXJmaWNpZSBkZSBsYSBwcm9waWVkYWQgKG1vZGVsbyAyKSwgdGVuaWVuZG8gZW4gY3VlbnRhIHF1ZSBlbCBSLWN1YWRyYWRvIGVzIG1heW9yIChSMj0gMC41NSlhbCBtb2RlbG8gcXVlIHRvbWEgY29tbyB2YWxvciBkZSBlbnRyYWRhIGxhIGNhbnRpZGFkIGRlIGN1YXJ0b3MoUjI9MC4yNykuDQogDQo=