TP2 EEA 2019

#knitr::opts_chunk$set(echo = T)
setwd("/home/andy/maestria/EEA2019/trabajos_practicos/TP-2/TP2/") #Seteamos directorio de trabajo
ar_properties<-readRDS("ar_properties.rds") #Cargamos las propiedades
  1. Regresion lineal multiple
ml <- lm(price ~ l3 + rooms + bathrooms + surface_total + surface_covered + property_type, ar_properties)
#summary(ml)

Todas las variables numéricas son significativas al igual que las de tipo de propiead, mientras que la mayor parte de las dummies también. Las variables dummies que no son significativas puede que no tengan mucha representatividad en el dataset, veamoslo.

sml <- summary(ml)
table(ar_properties$l3)[gsub("l3", "", rownames(sml$coefficients)[sml$coefficients[grep("l3", rownames(sml$coefficients)), 4] > 0.05])]

No parece estar relacionado con la representatividad en el dataset. El R2 de 0.78 y el F-statistic significativo hacen que se gane confianza en el modelo. Veamos que conviene vender:

pred <- c(1, 3, 2, 120, 120, 1)
precio_depto <- ml$coefficients[c("(Intercept)", "rooms", "bathrooms", "surface_total", "surface_covered", "property_typeDepartamento")]%*%pred
pred <- c(1, 1, 2, 3, 100, 80, 1)
precio_ph <- ml$coefficients[c("(Intercept)", "l3Balvanera", "rooms", "bathrooms", "surface_total", "surface_covered", "property_typePH")]%*%pred
data.frame(Depto=precio_depto, PH=precio_ph)

Conviene tener un depto para vender. Veamos que pasa si sacamos el barrio.

fit <- lm(price ~ rooms + bathrooms + surface_total + surface_covered + property_type, ar_properties)
summary(fit)

Todos los coeficientes son significativos. El R2 cae respecto de la inclusión del barrio. Evidentemente el barrio es una covariable importante a tener en cuenta en este modelo. 2) Creación de variables. Para seleccionar los barrios con precios altos, medios y bajos vamos a usar el desvío estandar. Los barrios que estén a un desvío estandar de la media, serán altos o bajos. El restó será medio.

ar_properties$precio_por_metro_cuadrado <- ar_properties$price/(ar_properties$surface_covered + 0.5*(ar_properties$surface_total - ar_properties$surface_covered))
precios_promedios_por_barrio <- aggregate(precio_por_metro_cuadrado ~ l3, ar_properties, mean) #Buscamos los precios promedio por metro cuadrado por barrio. La superficie descubierta suele contar como la mitad de la cubierta
precios_promedios_por_barrio
library(ggplot2)
ggplot(precios_promedios_por_barrio, aes(x = precio_por_metro_cuadrado)) + geom_histogram()
ggplot(precios_promedios_por_barrio, aes(x = precio_por_metro_cuadrado)) + geom_histogram() + scale_x_log10()
#ggplot(precios_promedios_por_barrio, aes(y = precio_por_metro_cuadrado)) + geom_boxplot()
lpm<-log(precios_promedios_por_barrio$precio_por_metro_cuadrado) #Lo transformamos con logaritmo para normalizarlo
s <- sd(lpm)
u <- mean(lpm)
precios_promedios_por_barrio$barrios <- ifelse(lpm > u + s, "alto", "bajo")
precios_promedios_por_barrio$barrios <- ifelse(lpm < u + s & lpm > u - s, "medio", precios_promedios_por_barrio$barrios)
rownames(precios_promedios_por_barrio) <- precios_promedios_por_barrio$l3
table(precios_promedios_por_barrio$barrios) #Cuantos barrios tenemos de cada categoria. No quedan muy balanceados.
ar_properties$barrios <- precios_promedios_por_barrio[ar_properties$l3, "barrios"]
table(ar_properties$barrios) #Cuantas propiedades tenemos de cada.
colnames(ar_properties)
fit <- lm(price ~ rooms + bathrooms + surface_total + surface_covered + property_type + barrios, ar_properties)
summary(fit)

Todos los coeficientes son significativos, con R2 relativamente alto y un F-statistic significativo. El promedio de precios es de

mean(ar_properties$price)

por lo que el residual standard error representa un

71160/mean(ar_properties$price)*100

porciento del precio promedio. Vemos que los residuos no están centrados en el cero. Graficando los residuos vemos que tienen estructura, con lo cual no pareciera ser un buen ajuste, más allá de los valores de R2 y el F-statistic.

plot(fit)

Veamos con l3

fit <- lm(price ~ rooms + bathrooms + surface_total + surface_covered + property_type + l3, ar_properties)
#summary(fit)

Para l3 en lugar del barrio, el R2 es un poco mejor (pero con tantos términos agregados puede ser simplemente un artefacto de la cantidad de covariables). El F-statistic da mas bajo que en el caso anterior y un error estandar residual menor. Vemos los residuos.

plot(fit)

Nuevamente tienen estructura, por lo que el ajuste no es muy bueno. Evidentemente el barrio tiene relevancia para el precio pero estas nuevas variables no logran generar un efecto frente al resto. Los dos modelos dan resultados similares. La nueva variable es más fácil de interpretar, pero requiere una clasificación previa arbitraria de los barrios respecto al precio promedio.

#construimos surface patio
ar_properties$surface_patio <- ar_properties$surface_total - ar_properties$surface_covered
summary(ar_properties$surface_patio)

No existen registros cuyo surface_total < surface_covered, pero en caso de que los hubiera, se podrían descartar esos registros.

fit <- lm(price ~ rooms + bathrooms + surface_patio + surface_covered + property_type + barrios, ar_properties)
summary(fit)
plot(fit)

Para facilitar la interpretación utilizamos la variable barrios en lugar de l3. Tanto surface_patio como surface_covered son significativos, con el coeficiente de patio aproximadamente un tercio del de covered. Usualmente se considera que una superficie descubierta es equivalente a media cubierta, pero acá podemos ver que debería ser menor aun. Por otro lado, se obtienen valores muy similares al ajuste anterior para todos los estadísticos, evidentemente la nueva variable no resuelve el problema de la alta correlación entre covered y total. 3) Evaluación del modelo. Los residuos anteriores muestran estructura por lo que un ajuste lineal del precio no parece adecuado. Los residuos muestran dependencia con las covariables y el Q-Q plot se separa de los valores esperados.

library(ggplot2)
library(tidyr)
library(purrr)
ar_properties %>%
keep(is.numeric) %>%
gather() %>%        
ggplot(aes(value)) +
  facet_wrap( ~ key, scales = "free") +
  geom_histogram()

Evidentemente ninguna de las variables es normal, por lo que no se cumplen los supuestos de la regresión lineal. Transformemos las variables.

fit <- lm(log(price) ~ log(rooms) + log(bathrooms) + log(surface_covered) + property_type + barrios + surface_patio, ar_properties)
summary(fit)
plot(fit)

Nuevamente todos los coeficientes son significativos, y se observa ahora si una distribución simétrica de residuos alrededor del 0. Además, se observa una mejora en R2 y un aumento del F-Statistic.

0.2346/mean(log(ar_properties$price))*100

Ahora el error residual solo implica el 2% del promedio de , mejorando el Q-Q plot y reduciendo la estructura en los errores. Evidentemente este ajuste es superior a los anteriores. 4) Dataframes anidados

library(modelr)
library(broom)
library(dplyr)
library(tidyr)
library(ggplot2)
library(purrr)
by_property_type <- ar_properties %>% 
  group_by(property_type) %>% 
  nest()

property_type_model <- function(df) {
  fit <- lm(price ~ rooms + bathrooms + surface_patio + surface_covered + barrios, df)
  return(fit)
}
by_property_type <- by_property_type %>% 
  mutate(model = map(data, property_type_model))
by_property_type <- by_property_type %>% 
mutate(
  resids = map2(data, model, add_residuals)
)
by_property_type
resids <- unnest(by_property_type, resids)
by_property_type %>% 
  mutate(glnc = map(model, glance)) %>% 
  unnest(glnc)
glnc <- by_property_type %>% 
  mutate(glnc = map(model, glance)) %>% 
  unnest(glnc) %>% 
  select(-c(data,model,resids))
glnc %>% 
  arrange(r.squared)
glnc %>% 
  ggplot(aes(property_type, r.squared)) + 
  geom_point()+
  theme(legend.position = "none")
map(by_property_type$model, plot)

El modelo lineal parece funcionar mejor para Departamentos que para Casas en base al R2. Observando los residuos vemos nuevamente que existe estructura en todos por lo que sería mejor transformar los datos para poder realizar los ajustes.

LS0tCnRpdGxlOiAiVFAyIEVFQTIwMTkgQW5kcsOpcyBSYWJpbm92aWNoIHkgRmVkZXJpY28gUmFiaW5vdmljaCIKb3V0cHV0OiAKICBodG1sX25vdGVib29rOiAKICAgIGZpZ19oZWlnaHQ6IDMKICAgIGZpZ193aWR0aDogNAplZGl0b3Jfb3B0aW9uczogCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQotLS0KVFAyIEVFQSAyMDE5CmBgYHtyfQoja25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUKQpzZXR3ZCgiL2hvbWUvYW5keS9tYWVzdHJpYS9FRUEyMDE5L3RyYWJham9zX3ByYWN0aWNvcy9UUC0yL1RQMi8iKSAjU2V0ZWFtb3MgZGlyZWN0b3JpbyBkZSB0cmFiYWpvCmFyX3Byb3BlcnRpZXM8LXJlYWRSRFMoImFyX3Byb3BlcnRpZXMucmRzIikgI0NhcmdhbW9zIGxhcyBwcm9waWVkYWRlcwpgYGAKMSkgUmVncmVzaW9uIGxpbmVhbCBtdWx0aXBsZQpgYGB7cn0KbWwgPC0gbG0ocHJpY2UgfiBsMyArIHJvb21zICsgYmF0aHJvb21zICsgc3VyZmFjZV90b3RhbCArIHN1cmZhY2VfY292ZXJlZCArIHByb3BlcnR5X3R5cGUsIGFyX3Byb3BlcnRpZXMpCiNzdW1tYXJ5KG1sKQpgYGAKVG9kYXMgbGFzIHZhcmlhYmxlcyBudW3DqXJpY2FzIHNvbiBzaWduaWZpY2F0aXZhcyBhbCBpZ3VhbCBxdWUgbGFzIGRlIHRpcG8gZGUgcHJvcGllYWQsIG1pZW50cmFzIHF1ZSBsYSBtYXlvciBwYXJ0ZSBkZSBsYXMgZHVtbWllcyB0YW1iacOpbi4gTGFzIHZhcmlhYmxlcyBkdW1taWVzIHF1ZSBubyBzb24gc2lnbmlmaWNhdGl2YXMgcHVlZGUgcXVlIG5vIHRlbmdhbiBtdWNoYSByZXByZXNlbnRhdGl2aWRhZCBlbiBlbCBkYXRhc2V0LCB2ZWFtb3Nsby4KYGBge3J9CnNtbCA8LSBzdW1tYXJ5KG1sKQp0YWJsZShhcl9wcm9wZXJ0aWVzJGwzKVtnc3ViKCJsMyIsICIiLCByb3duYW1lcyhzbWwkY29lZmZpY2llbnRzKVtzbWwkY29lZmZpY2llbnRzW2dyZXAoImwzIiwgcm93bmFtZXMoc21sJGNvZWZmaWNpZW50cykpLCA0XSA+IDAuMDVdKV0KYGBgCk5vIHBhcmVjZSBlc3RhciByZWxhY2lvbmFkbyBjb24gbGEgcmVwcmVzZW50YXRpdmlkYWQgZW4gZWwgZGF0YXNldC4KRWwgUjIgZGUgMC43OCB5IGVsIEYtc3RhdGlzdGljIHNpZ25pZmljYXRpdm8gaGFjZW4gcXVlIHNlIGdhbmUgY29uZmlhbnphIGVuIGVsIG1vZGVsby4KVmVhbW9zIHF1ZSBjb252aWVuZSB2ZW5kZXI6CmBgYHtyfQpwcmVkIDwtIGMoMSwgMywgMiwgMTIwLCAxMjAsIDEpCnByZWNpb19kZXB0byA8LSBtbCRjb2VmZmljaWVudHNbYygiKEludGVyY2VwdCkiLCAicm9vbXMiLCAiYmF0aHJvb21zIiwgInN1cmZhY2VfdG90YWwiLCAic3VyZmFjZV9jb3ZlcmVkIiwgInByb3BlcnR5X3R5cGVEZXBhcnRhbWVudG8iKV0lKiVwcmVkCnByZWQgPC0gYygxLCAxLCAyLCAzLCAxMDAsIDgwLCAxKQpwcmVjaW9fcGggPC0gbWwkY29lZmZpY2llbnRzW2MoIihJbnRlcmNlcHQpIiwgImwzQmFsdmFuZXJhIiwgInJvb21zIiwgImJhdGhyb29tcyIsICJzdXJmYWNlX3RvdGFsIiwgInN1cmZhY2VfY292ZXJlZCIsICJwcm9wZXJ0eV90eXBlUEgiKV0lKiVwcmVkCmRhdGEuZnJhbWUoRGVwdG89cHJlY2lvX2RlcHRvLCBQSD1wcmVjaW9fcGgpCmBgYApDb252aWVuZSB0ZW5lciB1biBkZXB0byBwYXJhIHZlbmRlci4KVmVhbW9zIHF1ZSBwYXNhIHNpIHNhY2Ftb3MgZWwgYmFycmlvLgpgYGB7cn0KZml0IDwtIGxtKHByaWNlIH4gcm9vbXMgKyBiYXRocm9vbXMgKyBzdXJmYWNlX3RvdGFsICsgc3VyZmFjZV9jb3ZlcmVkICsgcHJvcGVydHlfdHlwZSwgYXJfcHJvcGVydGllcykKc3VtbWFyeShmaXQpCmBgYApUb2RvcyBsb3MgY29lZmljaWVudGVzIHNvbiBzaWduaWZpY2F0aXZvcy4gRWwgUjIgY2FlIHJlc3BlY3RvIGRlIGxhIGluY2x1c2nDs24gZGVsIGJhcnJpby4gRXZpZGVudGVtZW50ZSBlbCBiYXJyaW8gZXMgdW5hIGNvdmFyaWFibGUgaW1wb3J0YW50ZSBhIHRlbmVyIGVuIGN1ZW50YSBlbiBlc3RlIG1vZGVsby4KMikgQ3JlYWNpw7NuIGRlIHZhcmlhYmxlcy4KUGFyYSBzZWxlY2Npb25hciBsb3MgYmFycmlvcyBjb24gcHJlY2lvcyBhbHRvcywgbWVkaW9zIHkgYmFqb3MgdmFtb3MgYSB1c2FyIGVsIGRlc3bDrW8gZXN0YW5kYXIuIExvcyBiYXJyaW9zIHF1ZSBlc3TDqW4gYSB1biBkZXN2w61vIGVzdGFuZGFyIGRlIGxhIG1lZGlhLCBzZXLDoW4gYWx0b3MgbyBiYWpvcy4gRWwgcmVzdMOzIHNlcsOhIG1lZGlvLgpgYGB7cn0KYXJfcHJvcGVydGllcyRwcmVjaW9fcG9yX21ldHJvX2N1YWRyYWRvIDwtIGFyX3Byb3BlcnRpZXMkcHJpY2UvKGFyX3Byb3BlcnRpZXMkc3VyZmFjZV9jb3ZlcmVkICsgMC41Kihhcl9wcm9wZXJ0aWVzJHN1cmZhY2VfdG90YWwgLSBhcl9wcm9wZXJ0aWVzJHN1cmZhY2VfY292ZXJlZCkpCnByZWNpb3NfcHJvbWVkaW9zX3Bvcl9iYXJyaW8gPC0gYWdncmVnYXRlKHByZWNpb19wb3JfbWV0cm9fY3VhZHJhZG8gfiBsMywgYXJfcHJvcGVydGllcywgbWVhbikgI0J1c2NhbW9zIGxvcyBwcmVjaW9zIHByb21lZGlvIHBvciBtZXRybyBjdWFkcmFkbyBwb3IgYmFycmlvLiBMYSBzdXBlcmZpY2llIGRlc2N1YmllcnRhIHN1ZWxlIGNvbnRhciBjb21vIGxhIG1pdGFkIGRlIGxhIGN1YmllcnRhCnByZWNpb3NfcHJvbWVkaW9zX3Bvcl9iYXJyaW8KbGlicmFyeShnZ3Bsb3QyKQpnZ3Bsb3QocHJlY2lvc19wcm9tZWRpb3NfcG9yX2JhcnJpbywgYWVzKHggPSBwcmVjaW9fcG9yX21ldHJvX2N1YWRyYWRvKSkgKyBnZW9tX2hpc3RvZ3JhbSgpCmdncGxvdChwcmVjaW9zX3Byb21lZGlvc19wb3JfYmFycmlvLCBhZXMoeCA9IHByZWNpb19wb3JfbWV0cm9fY3VhZHJhZG8pKSArIGdlb21faGlzdG9ncmFtKCkgKyBzY2FsZV94X2xvZzEwKCkKI2dncGxvdChwcmVjaW9zX3Byb21lZGlvc19wb3JfYmFycmlvLCBhZXMoeSA9IHByZWNpb19wb3JfbWV0cm9fY3VhZHJhZG8pKSArIGdlb21fYm94cGxvdCgpCmxwbTwtbG9nKHByZWNpb3NfcHJvbWVkaW9zX3Bvcl9iYXJyaW8kcHJlY2lvX3Bvcl9tZXRyb19jdWFkcmFkbykgI0xvIHRyYW5zZm9ybWFtb3MgY29uIGxvZ2FyaXRtbyBwYXJhIG5vcm1hbGl6YXJsbwpzIDwtIHNkKGxwbSkKdSA8LSBtZWFuKGxwbSkKcHJlY2lvc19wcm9tZWRpb3NfcG9yX2JhcnJpbyRiYXJyaW9zIDwtIGlmZWxzZShscG0gPiB1ICsgcywgImFsdG8iLCAiYmFqbyIpCnByZWNpb3NfcHJvbWVkaW9zX3Bvcl9iYXJyaW8kYmFycmlvcyA8LSBpZmVsc2UobHBtIDwgdSArIHMgJiBscG0gPiB1IC0gcywgIm1lZGlvIiwgcHJlY2lvc19wcm9tZWRpb3NfcG9yX2JhcnJpbyRiYXJyaW9zKQpyb3duYW1lcyhwcmVjaW9zX3Byb21lZGlvc19wb3JfYmFycmlvKSA8LSBwcmVjaW9zX3Byb21lZGlvc19wb3JfYmFycmlvJGwzCnRhYmxlKHByZWNpb3NfcHJvbWVkaW9zX3Bvcl9iYXJyaW8kYmFycmlvcykgI0N1YW50b3MgYmFycmlvcyB0ZW5lbW9zIGRlIGNhZGEgY2F0ZWdvcmlhLiBObyBxdWVkYW4gbXV5IGJhbGFuY2VhZG9zLgphcl9wcm9wZXJ0aWVzJGJhcnJpb3MgPC0gcHJlY2lvc19wcm9tZWRpb3NfcG9yX2JhcnJpb1thcl9wcm9wZXJ0aWVzJGwzLCAiYmFycmlvcyJdCnRhYmxlKGFyX3Byb3BlcnRpZXMkYmFycmlvcykgI0N1YW50YXMgcHJvcGllZGFkZXMgdGVuZW1vcyBkZSBjYWRhLgpgYGAKYGBge3J9CmNvbG5hbWVzKGFyX3Byb3BlcnRpZXMpCmZpdCA8LSBsbShwcmljZSB+IHJvb21zICsgYmF0aHJvb21zICsgc3VyZmFjZV90b3RhbCArIHN1cmZhY2VfY292ZXJlZCArIHByb3BlcnR5X3R5cGUgKyBiYXJyaW9zLCBhcl9wcm9wZXJ0aWVzKQpzdW1tYXJ5KGZpdCkKYGBgClRvZG9zIGxvcyBjb2VmaWNpZW50ZXMgc29uIHNpZ25pZmljYXRpdm9zLCBjb24gUjIgcmVsYXRpdmFtZW50ZSBhbHRvIHkgdW4gRi1zdGF0aXN0aWMgc2lnbmlmaWNhdGl2by4KRWwgcHJvbWVkaW8gZGUgcHJlY2lvcyBlcyBkZSAKYGBge3J9Cm1lYW4oYXJfcHJvcGVydGllcyRwcmljZSkKYGBgCnBvciBsbyBxdWUgZWwgcmVzaWR1YWwgc3RhbmRhcmQgZXJyb3IgcmVwcmVzZW50YSB1biAKYGBge3J9CjcxMTYwL21lYW4oYXJfcHJvcGVydGllcyRwcmljZSkqMTAwCmBgYApwb3JjaWVudG8gZGVsIHByZWNpbyBwcm9tZWRpby4gClZlbW9zIHF1ZSBsb3MgcmVzaWR1b3Mgbm8gZXN0w6FuIGNlbnRyYWRvcyBlbiBlbCBjZXJvLiBHcmFmaWNhbmRvIGxvcyByZXNpZHVvcyB2ZW1vcyBxdWUgdGllbmVuIGVzdHJ1Y3R1cmEsIGNvbiBsbyBjdWFsIG5vIHBhcmVjaWVyYSBzZXIgdW4gYnVlbiBhanVzdGUsIG3DoXMgYWxsw6EgZGUgbG9zIHZhbG9yZXMgZGUgUjIgeSBlbCBGLXN0YXRpc3RpYy4KYGBge3J9CnBsb3QoZml0KQpgYGAKVmVhbW9zIGNvbiBsMwpgYGB7cn0KZml0IDwtIGxtKHByaWNlIH4gcm9vbXMgKyBiYXRocm9vbXMgKyBzdXJmYWNlX3RvdGFsICsgc3VyZmFjZV9jb3ZlcmVkICsgcHJvcGVydHlfdHlwZSArIGwzLCBhcl9wcm9wZXJ0aWVzKQojc3VtbWFyeShmaXQpCmBgYApQYXJhIGwzIGVuIGx1Z2FyIGRlbCBiYXJyaW8sIGVsIFIyIGVzIHVuIHBvY28gbWVqb3IgKHBlcm8gY29uIHRhbnRvcyB0w6lybWlub3MgYWdyZWdhZG9zIHB1ZWRlIHNlciBzaW1wbGVtZW50ZSB1biBhcnRlZmFjdG8gZGUgbGEgY2FudGlkYWQgZGUgY292YXJpYWJsZXMpLiBFbCBGLXN0YXRpc3RpYyBkYSBtYXMgYmFqbyBxdWUgZW4gZWwgY2FzbyBhbnRlcmlvciB5IHVuIGVycm9yIGVzdGFuZGFyIHJlc2lkdWFsIG1lbm9yLiBWZW1vcyBsb3MgcmVzaWR1b3MuCmBgYHtyfQpwbG90KGZpdCkKYGBgCk51ZXZhbWVudGUgdGllbmVuIGVzdHJ1Y3R1cmEsIHBvciBsbyBxdWUgZWwgYWp1c3RlIG5vIGVzIG11eSBidWVuby4gRXZpZGVudGVtZW50ZSBlbCBiYXJyaW8gdGllbmUgcmVsZXZhbmNpYSBwYXJhIGVsIHByZWNpbyBwZXJvIGVzdGFzIG51ZXZhcyB2YXJpYWJsZXMgbm8gbG9ncmFuIGdlbmVyYXIgdW4gZWZlY3RvIGZyZW50ZSBhbCByZXN0by4gTG9zIGRvcyBtb2RlbG9zIGRhbiByZXN1bHRhZG9zIHNpbWlsYXJlcy4gTGEgbnVldmEgdmFyaWFibGUgZXMgbcOhcyBmw6FjaWwgZGUgaW50ZXJwcmV0YXIsIHBlcm8gcmVxdWllcmUgdW5hIGNsYXNpZmljYWNpw7NuIHByZXZpYSBhcmJpdHJhcmlhIGRlIGxvcyBiYXJyaW9zIHJlc3BlY3RvIGFsIHByZWNpbyBwcm9tZWRpby4KYGBge3J9CiNjb25zdHJ1aW1vcyBzdXJmYWNlIHBhdGlvCmFyX3Byb3BlcnRpZXMkc3VyZmFjZV9wYXRpbyA8LSBhcl9wcm9wZXJ0aWVzJHN1cmZhY2VfdG90YWwgLSBhcl9wcm9wZXJ0aWVzJHN1cmZhY2VfY292ZXJlZApzdW1tYXJ5KGFyX3Byb3BlcnRpZXMkc3VyZmFjZV9wYXRpbykKYGBgCk5vIGV4aXN0ZW4gcmVnaXN0cm9zIGN1eW8gc3VyZmFjZV90b3RhbCA8IHN1cmZhY2VfY292ZXJlZCwgcGVybyBlbiBjYXNvIGRlIHF1ZSBsb3MgaHViaWVyYSwgc2UgcG9kcsOtYW4gZGVzY2FydGFyIGVzb3MgcmVnaXN0cm9zLgpgYGB7cn0KZml0IDwtIGxtKHByaWNlIH4gcm9vbXMgKyBiYXRocm9vbXMgKyBzdXJmYWNlX3BhdGlvICsgc3VyZmFjZV9jb3ZlcmVkICsgcHJvcGVydHlfdHlwZSArIGJhcnJpb3MsIGFyX3Byb3BlcnRpZXMpCnN1bW1hcnkoZml0KQpwbG90KGZpdCkKYGBgClBhcmEgZmFjaWxpdGFyIGxhIGludGVycHJldGFjacOzbiB1dGlsaXphbW9zIGxhIHZhcmlhYmxlIGJhcnJpb3MgZW4gbHVnYXIgZGUgbDMuIFRhbnRvIHN1cmZhY2VfcGF0aW8gY29tbyBzdXJmYWNlX2NvdmVyZWQgc29uIHNpZ25pZmljYXRpdm9zLCBjb24gZWwgY29lZmljaWVudGUgZGUgcGF0aW8gYXByb3hpbWFkYW1lbnRlIHVuIHRlcmNpbyBkZWwgZGUgY292ZXJlZC4gVXN1YWxtZW50ZSBzZSBjb25zaWRlcmEgcXVlIHVuYSBzdXBlcmZpY2llIGRlc2N1YmllcnRhIGVzIGVxdWl2YWxlbnRlIGEgbWVkaWEgY3ViaWVydGEsIHBlcm8gYWPDoSBwb2RlbW9zIHZlciBxdWUgZGViZXLDrWEgc2VyIG1lbm9yIGF1bi4gUG9yIG90cm8gbGFkbywgc2Ugb2J0aWVuZW4gdmFsb3JlcyBtdXkgc2ltaWxhcmVzIGFsIGFqdXN0ZSBhbnRlcmlvciBwYXJhIHRvZG9zIGxvcyBlc3RhZMOtc3RpY29zLCBldmlkZW50ZW1lbnRlIGxhIG51ZXZhIHZhcmlhYmxlIG5vIHJlc3VlbHZlIGVsIHByb2JsZW1hIGRlIGxhIGFsdGEgY29ycmVsYWNpw7NuIGVudHJlIGNvdmVyZWQgeSB0b3RhbC4KMykgRXZhbHVhY2nDs24gZGVsIG1vZGVsby4KTG9zIHJlc2lkdW9zIGFudGVyaW9yZXMgbXVlc3RyYW4gZXN0cnVjdHVyYSBwb3IgbG8gcXVlIHVuIGFqdXN0ZSBsaW5lYWwgZGVsIHByZWNpbyBubyBwYXJlY2UgYWRlY3VhZG8uIExvcyByZXNpZHVvcyBtdWVzdHJhbiBkZXBlbmRlbmNpYSBjb24gbGFzIGNvdmFyaWFibGVzIHkgZWwgUS1RIHBsb3Qgc2Ugc2VwYXJhIGRlIGxvcyB2YWxvcmVzIGVzcGVyYWRvcy4KYGBge3J9CmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeSh0aWR5cikKbGlicmFyeShwdXJycikKYXJfcHJvcGVydGllcyAlPiUKa2VlcChpcy5udW1lcmljKSAlPiUKZ2F0aGVyKCkgJT4lICAgICAgICAKZ2dwbG90KGFlcyh2YWx1ZSkpICsKICBmYWNldF93cmFwKCB+IGtleSwgc2NhbGVzID0gImZyZWUiKSArCiAgZ2VvbV9oaXN0b2dyYW0oKQpgYGAKRXZpZGVudGVtZW50ZSBuaW5ndW5hIGRlIGxhcyB2YXJpYWJsZXMgZXMgbm9ybWFsLCBwb3IgbG8gcXVlIG5vIHNlIGN1bXBsZW4gbG9zIHN1cHVlc3RvcyBkZSBsYSByZWdyZXNpw7NuIGxpbmVhbC4gVHJhbnNmb3JtZW1vcyBsYXMgdmFyaWFibGVzLgpgYGB7cn0KZml0IDwtIGxtKGxvZyhwcmljZSkgfiBsb2cocm9vbXMpICsgbG9nKGJhdGhyb29tcykgKyBsb2coc3VyZmFjZV9jb3ZlcmVkKSArIHByb3BlcnR5X3R5cGUgKyBiYXJyaW9zICsgc3VyZmFjZV9wYXRpbywgYXJfcHJvcGVydGllcykKc3VtbWFyeShmaXQpCnBsb3QoZml0KQpgYGAKTnVldmFtZW50ZSB0b2RvcyBsb3MgY29lZmljaWVudGVzIHNvbiBzaWduaWZpY2F0aXZvcywgeSBzZSBvYnNlcnZhIGFob3JhIHNpIHVuYSBkaXN0cmlidWNpw7NuIHNpbcOpdHJpY2EgZGUgcmVzaWR1b3MgYWxyZWRlZG9yIGRlbCAwLiBBZGVtw6FzLCBzZSBvYnNlcnZhIHVuYSBtZWpvcmEgZW4gUjIgeSB1biBhdW1lbnRvIGRlbCBGLVN0YXRpc3RpYy4KYGBge3J9CjAuMjM0Ni9tZWFuKGxvZyhhcl9wcm9wZXJ0aWVzJHByaWNlKSkqMTAwCmBgYApBaG9yYSBlbCBlcnJvciByZXNpZHVhbCBzb2xvIGltcGxpY2EgZWwgMiUgZGVsIHByb21lZGlvIGRlICwgbWVqb3JhbmRvIGVsIFEtUSBwbG90IHkgcmVkdWNpZW5kbyBsYSBlc3RydWN0dXJhIGVuIGxvcyBlcnJvcmVzLiBFdmlkZW50ZW1lbnRlIGVzdGUgYWp1c3RlIGVzIHN1cGVyaW9yIGEgbG9zIGFudGVyaW9yZXMuCjQpIERhdGFmcmFtZXMgYW5pZGFkb3MKYGBge3J9CmxpYnJhcnkobW9kZWxyKQpsaWJyYXJ5KGJyb29tKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHRpZHlyKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkocHVycnIpCmJ5X3Byb3BlcnR5X3R5cGUgPC0gYXJfcHJvcGVydGllcyAlPiUgCiAgZ3JvdXBfYnkocHJvcGVydHlfdHlwZSkgJT4lIAogIG5lc3QoKQoKcHJvcGVydHlfdHlwZV9tb2RlbCA8LSBmdW5jdGlvbihkZikgewogIGZpdCA8LSBsbShwcmljZSB+IHJvb21zICsgYmF0aHJvb21zICsgc3VyZmFjZV9wYXRpbyArIHN1cmZhY2VfY292ZXJlZCArIGJhcnJpb3MsIGRmKQogIHJldHVybihmaXQpCn0KYnlfcHJvcGVydHlfdHlwZSA8LSBieV9wcm9wZXJ0eV90eXBlICU+JSAKICBtdXRhdGUobW9kZWwgPSBtYXAoZGF0YSwgcHJvcGVydHlfdHlwZV9tb2RlbCkpCmJ5X3Byb3BlcnR5X3R5cGUgPC0gYnlfcHJvcGVydHlfdHlwZSAlPiUgCm11dGF0ZSgKICByZXNpZHMgPSBtYXAyKGRhdGEsIG1vZGVsLCBhZGRfcmVzaWR1YWxzKQopCmJ5X3Byb3BlcnR5X3R5cGUKcmVzaWRzIDwtIHVubmVzdChieV9wcm9wZXJ0eV90eXBlLCByZXNpZHMpCmJ5X3Byb3BlcnR5X3R5cGUgJT4lIAogIG11dGF0ZShnbG5jID0gbWFwKG1vZGVsLCBnbGFuY2UpKSAlPiUgCiAgdW5uZXN0KGdsbmMpCmdsbmMgPC0gYnlfcHJvcGVydHlfdHlwZSAlPiUgCiAgbXV0YXRlKGdsbmMgPSBtYXAobW9kZWwsIGdsYW5jZSkpICU+JSAKICB1bm5lc3QoZ2xuYykgJT4lIAogIHNlbGVjdCgtYyhkYXRhLG1vZGVsLHJlc2lkcykpCmdsbmMgJT4lIAogIGFycmFuZ2Uoci5zcXVhcmVkKQpnbG5jICU+JSAKICBnZ3Bsb3QoYWVzKHByb3BlcnR5X3R5cGUsIHIuc3F1YXJlZCkpICsgCiAgZ2VvbV9wb2ludCgpKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKbWFwKGJ5X3Byb3BlcnR5X3R5cGUkbW9kZWwsIHBsb3QpCmBgYApFbCBtb2RlbG8gbGluZWFsIHBhcmVjZSBmdW5jaW9uYXIgbWVqb3IgcGFyYSBEZXBhcnRhbWVudG9zIHF1ZSBwYXJhIENhc2FzIGVuIGJhc2UgYWwgUjIuIE9ic2VydmFuZG8gbG9zIHJlc2lkdW9zIHZlbW9zIG51ZXZhbWVudGUgcXVlIGV4aXN0ZSBlc3RydWN0dXJhIGVuIHRvZG9zIHBvciBsbyBxdWUgc2Vyw61hIG1lam9yIHRyYW5zZm9ybWFyIGxvcyBkYXRvcyBwYXJhIHBvZGVyIHJlYWxpemFyIGxvcyBhanVzdGVzLgo=