library(tidyverse)

Para este ejercicio utilizaremos los datos provistos por Properati: https://www.properati.com.ar/data/

(Primero acondicionamos la base original, para quedarnos con una base más fácil de trabajar, y que contiene unicamente los datos interesantes. También eliminamos previamente los outliers)

df <- read_rds('../fuentes/datos_properati.RDS')

df %>% sample_n(10)

Primer modelo

lm_fit <- lm(price~ l3+ rooms + bathrooms + surface_total + property_type,data = df)
summary(lm_fit)

Call:
lm(formula = price ~ l3 + rooms + bathrooms + surface_total + 
    property_type, data = df)

Residuals:
    Min      1Q  Median      3Q     Max 
-432633  -34617   -2999   25213  583297 

Coefficients:
                           Estimate Std. Error t value Pr(>|t|)    
(Intercept)               -113646.5     4920.4 -23.097  < 2e-16 ***
l3Agronomía                 -7650.2     9089.3  -0.842 0.399975    
l3Almagro                   -3300.7     4414.0  -0.748 0.454598    
l3Balvanera                -21220.7     4677.0  -4.537 5.71e-06 ***
l3Barracas                  -8272.3     5498.9  -1.504 0.132499    
l3Barrio Norte              53994.3     4539.3  11.895  < 2e-16 ***
l3Belgrano                  71899.8     4401.8  16.334  < 2e-16 ***
l3Boca                     -45747.9     7271.8  -6.291 3.18e-10 ***
l3Boedo                    -17793.3     5363.9  -3.317 0.000910 ***
l3Caballito                  6870.9     4420.2   1.554 0.120093    
l3Catalinas                -97528.2    34489.4  -2.828 0.004689 ** 
l3Centro / Microcentro     -21833.7     6967.9  -3.133 0.001728 ** 
l3Chacarita                 12744.0     5445.6   2.340 0.019275 *  
l3Coghlan                   37410.3     5613.6   6.664 2.69e-11 ***
l3Colegiales                30624.4     4949.3   6.188 6.16e-10 ***
l3Congreso                 -29213.2     5646.4  -5.174 2.30e-07 ***
l3Constitución             -44764.9     6496.3  -6.891 5.62e-12 ***
l3Flores                   -22964.2     4661.6  -4.926 8.41e-07 ***
l3Floresta                 -31310.0     5209.2  -6.010 1.86e-09 ***
l3Las Cañitas               91699.5     6046.1  15.167  < 2e-16 ***
l3Liniers                  -21533.4     5514.6  -3.905 9.44e-05 ***
l3Mataderos                -39034.8     5573.9  -7.003 2.54e-12 ***
l3Monserrat                -27085.3     5372.0  -5.042 4.63e-07 ***
l3Monte Castro             -10307.5     6114.1  -1.686 0.091832 .  
l3Nuñez                     57485.1     4685.8  12.268  < 2e-16 ***
l3Once                     -24995.2     5606.2  -4.458 8.27e-06 ***
l3Palermo                   68847.6     4337.9  15.871  < 2e-16 ***
l3Parque Avellaneda        -36773.2     7808.1  -4.710 2.49e-06 ***
l3Parque Centenario        -10925.2     5155.1  -2.119 0.034071 *  
l3Parque Chacabuco         -25904.2     5460.9  -4.744 2.11e-06 ***
l3Parque Chas                4534.9     7751.6   0.585 0.558535    
l3Parque Patricios         -37629.6     6138.5  -6.130 8.85e-10 ***
l3Paternal                  -9966.1     5332.8  -1.869 0.061653 .  
l3Pompeya                  -84596.6     8257.5 -10.245  < 2e-16 ***
l3Puerto Madero            271162.6     5230.3  51.845  < 2e-16 ***
l3Recoleta                  72469.2     4477.7  16.184  < 2e-16 ***
l3Retiro                    35666.4     5423.9   6.576 4.89e-11 ***
l3Saavedra                  16524.2     5049.7   3.272 0.001068 ** 
l3San Cristobal            -21702.8     5092.0  -4.262 2.03e-05 ***
l3San Nicolás              -22350.7     5311.3  -4.208 2.58e-05 ***
l3San Telmo                 -1696.0     5011.4  -0.338 0.735048    
l3Tribunales               -29930.0     9171.0  -3.264 0.001101 ** 
l3Velez Sarsfield          -34345.3     8531.7  -4.026 5.69e-05 ***
l3Versalles                -23454.2     6945.3  -3.377 0.000733 ***
l3Villa Crespo               3563.2     4436.8   0.803 0.421921    
l3Villa del Parque          -4274.9     5001.2  -0.855 0.392675    
l3Villa Devoto              10170.4     4939.6   2.059 0.039506 *  
l3Villa General Mitre      -25922.9     6989.1  -3.709 0.000208 ***
l3Villa Lugano             -83540.1     6714.1 -12.443  < 2e-16 ***
l3Villa Luro                -9151.8     5554.2  -1.648 0.099415 .  
l3Villa Ortuzar             14436.0     7017.6   2.057 0.039679 *  
l3Villa Pueyrredón           5669.8     5496.6   1.032 0.302309    
l3Villa Real               -19969.3     8984.6  -2.223 0.026247 *  
l3Villa Riachuelo          -41512.4    17645.2  -2.353 0.018646 *  
l3Villa Santa Rita         -12362.7     6559.1  -1.885 0.059459 .  
l3Villa Soldati           -127528.0    19467.4  -6.551 5.78e-11 ***
l3Villa Urquiza             28531.8     4540.9   6.283 3.35e-10 ***
rooms                        2959.2      434.8   6.806 1.02e-11 ***
bathrooms                   39771.6      651.8  61.014  < 2e-16 ***
surface_total                1934.2       12.7 152.267  < 2e-16 ***
property_typeDepartamento   90449.7     2251.4  40.175  < 2e-16 ***
property_typePH             38887.8     2332.4  16.673  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 68420 on 45842 degrees of freedom
Multiple R-squared:  0.7639,    Adjusted R-squared:  0.7636 
F-statistic:  2431 on 61 and 45842 DF,  p-value: < 2.2e-16

¿ Qué pasó con las variables no numéricas? ¿Son significativos los estimadores? ¿cuales? ¿Cómo se leen los valores de los estimadores?

\[ \Delta y = \beta_1 \Delta x \]

Feature engineering.

Se denomina ingeniería de features a la construcción de nuevas variables a partir de las originales.

Por ejemplo, dado que muchos de los barrios no explican significativamente los cambios en los precios, no esta bueno conservarlos todos. A su vez, no sabemos respecto a qué barrio se compara.

Una solución puede ser agrupar los barrios en tres categorías respecto a su efecto en el precio:

  • Alto
  • Medio
  • Bajo

En particular, podemos notar de esta primera regresión que algunos barrios tienen un efecto significativo en subir el valor de la propiedad, como Belgrano o Recoleta.

Para construir la nueva variable, podemos ver el precio promedio del metro cuadrado por barrio


df_barrios <- df %>% 
  group_by(l3) %>% 
  summarise(precio_m2 = mean(price/surface_total)) 
  
ggplot(df_barrios,aes(precio_m2)) +
  geom_dotplot(method = 'histodot', binwidth = 100)



summary(df_barrios$precio_m2)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   1093    2062    2204    2358    2579    5472 

Con este gráfico vemos que que hay muchos barrios con un precio promedio cercano a 2500 dólares el \(m^2\).

Podemos dividr los tres grupos al rededor de los quartiles 1 y 3.

  • <2000 bajo
  • 2000-2500 medio
  • >2500 alto
df_barrios <- df_barrios %>% 
  mutate(barrio= case_when(precio_m2<=2100 ~ 'bajo',
                           precio_m2>2100 & precio_m2<2400 ~ 'medio',
                          #between(precio_m2,2100,2400)~'medio', # Otra forma de escribirlo
                           precio_m2>=2400 ~ 'alto'))

df_barrios %>% 
  sample_n(10)

Con esta nueva variable podemos modificar la tabla original.

df <- df %>% 
  left_join(df_barrios, by='l3')

y volvemos a calcular el modelo

lm_fit <- lm(price~ barrio+ rooms + bathrooms + surface_total + property_type,data = df)

summary(lm_fit)

Call:
lm(formula = price ~ barrio + rooms + bathrooms + surface_total + 
    property_type, data = df)

Residuals:
    Min      1Q  Median      3Q     Max 
-503397  -37713   -8157   25437  722990 

Coefficients:
                           Estimate Std. Error t value Pr(>|t|)    
(Intercept)               -91188.38    2767.07  -32.95  < 2e-16 ***
barriobajo                -74794.81    1454.63  -51.42  < 2e-16 ***
barriomedio               -54930.91     936.27  -58.67  < 2e-16 ***
rooms                      -2793.72     482.48   -5.79 7.07e-09 ***
bathrooms                  44048.54     731.31   60.23  < 2e-16 ***
surface_total               2109.92      14.16  149.05  < 2e-16 ***
property_typeDepartamento 106723.05    2493.37   42.80  < 2e-16 ***
property_typePH            46226.80    2620.10   17.64  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 77360 on 45896 degrees of freedom
Multiple R-squared:  0.6978,    Adjusted R-squared:  0.6977 
F-statistic: 1.514e+04 on 7 and 45896 DF,  p-value: < 2.2e-16

Si queremos que compare contra ‘barrio medio’ podemos convertir la variable en factor y explicitar los niveles

df <- df %>% 
  mutate(barrio = factor(barrio, levels = c('medio', 'alto','bajo')))

lm_fit <- lm(price~ barrio+ rooms + bathrooms + surface_total + property_type,data = df)

summary(lm_fit)

Call:
lm(formula = price ~ barrio + rooms + bathrooms + surface_total + 
    property_type, data = df)

Residuals:
    Min      1Q  Median      3Q     Max 
-503397  -37713   -8157   25437  722990 

Coefficients:
                            Estimate Std. Error t value Pr(>|t|)    
(Intercept)               -146119.29    2747.61  -53.18  < 2e-16 ***
barrioalto                  54930.91     936.27   58.67  < 2e-16 ***
barriobajo                 -19863.90    1585.84  -12.53  < 2e-16 ***
rooms                       -2793.72     482.48   -5.79 7.07e-09 ***
bathrooms                   44048.54     731.31   60.23  < 2e-16 ***
surface_total                2109.92      14.16  149.05  < 2e-16 ***
property_typeDepartamento  106723.05    2493.37   42.80  < 2e-16 ***
property_typePH             46226.80    2620.10   17.64  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 77360 on 45896 degrees of freedom
Multiple R-squared:  0.6978,    Adjusted R-squared:  0.6977 
F-statistic: 1.514e+04 on 7 and 45896 DF,  p-value: < 2.2e-16

Cuál es el mejor modelo? Con los barrios colapsados en tres niveles, o con todos los barrios?

Transformaciones log.

df %>% 
  sample_n(10000) %>% 
ggplot(., aes(surface_total, price))+
  geom_point(alpha=0.75)+
  geom_smooth(method = 'lm')

Nuestros datos no tienen varianza constante, tienen heterocedasticidad. Que la varianza sea constante es uno de los supestos del modelo lineal.

df %>% 
  sample_n(10000) %>% 
ggplot(., aes(surface_total, price))+
  geom_point(alpha=0.75)+
  geom_smooth(method = 'lm')+
  scale_y_log10()+
  scale_x_log10()

La transformación logarítmica parece ser una buena idea para nuestros datos

lm_fit <- lm(log(price)~ barrio+ rooms + bathrooms + log(surface_total) + property_type,data = df)

summary(lm_fit)

Call:
lm(formula = log(price) ~ barrio + rooms + bathrooms + log(surface_total) + 
    property_type, data = df)

Residuals:
    Min      1Q  Median      3Q     Max 
-1.3017 -0.1615 -0.0051  0.1486  1.4464 

Coefficients:
                           Estimate Std. Error t value Pr(>|t|)    
(Intercept)                8.082582   0.018153 445.255  < 2e-16 ***
barrioalto                 0.256069   0.003035  84.366  < 2e-16 ***
barriobajo                -0.130626   0.005141 -25.409  < 2e-16 ***
rooms                     -0.016385   0.001712  -9.569  < 2e-16 ***
bathrooms                  0.120528   0.002327  51.791  < 2e-16 ***
log(surface_total)         0.833111   0.004455 187.000  < 2e-16 ***
property_typeDepartamento  0.220169   0.007871  27.974  < 2e-16 ***
property_typePH            0.022008   0.008358   2.633  0.00846 ** 
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.2508 on 45896 degrees of freedom
Multiple R-squared:  0.7833,    Adjusted R-squared:  0.7833 
F-statistic: 2.371e+04 on 7 and 45896 DF,  p-value: < 2.2e-16
  • El \(R^2\) pasó de 0.6977 a 0.7833!!

  • Cómo se interpretan ahora los nuevos parámetros?

Modelo Variable Dependiente Variable Independiente Interpretación \(\beta_1\)
Nivel-Nivel \(y\) \(x\) \(\Delta y = \beta_1 \Delta x\)
Nivel-log \(y\) \(log(x)\) \(\Delta y = (\beta_1/100)\% \Delta x\)
Log-nivel \(log(y)\) \(x\) \(\% \Delta y = (100 \beta_1) \Delta x\)
Log-log \(log(y)\) \(log(x)\) \(\% \Delta y = \beta_1 \% \Delta x\)

Predicciones

Para predecir un nuevo caso, podemos construir un dataframe con las variables. Por ejemplo

caso_nuevo <- tibble(barrio='alto',
       rooms=2,
       bathrooms=2,
       property_type='Departamento',
       surface_total=78) 

predict(lm_fit,newdata = caso_nuevo)
       1 
12.39673 

Pero debemos recordar que este es el valor del logarítmo del percio. Por lo tanto tenemos que realizar el caminio inverso con la función exp

exp(predict(lm_fit,caso_nuevo))
       1 
242008.3 

Para seguir practicando

Un problema de lo que vimos en esta práctica es que las salidas de summary(lm_fit) es una impresión en la consola. Es muy difícil seguir trabajando con esos resultados. Para resolver esto hay un par de librerías que incorporan el modelado lineal al flujo del tidyverse:

LS0tCnRpdGxlOiBNb2RlbG8gTGluZWFsCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCmRhdGU6ICIiCnN1YnRpdGxlOiBQcsOhY3RpY2EgR3VpYWRhCi0tLQoKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkodGlkeXZlcnNlKQpgYGAKCgpQYXJhIGVzdGUgZWplcmNpY2lvIHV0aWxpemFyZW1vcyBsb3MgZGF0b3MgcHJvdmlzdG9zIHBvciBQcm9wZXJhdGk6IGh0dHBzOi8vd3d3LnByb3BlcmF0aS5jb20uYXIvZGF0YS8KCihQcmltZXJvIGFjb25kaWNpb25hbW9zIGxhIGJhc2Ugb3JpZ2luYWwsIHBhcmEgcXVlZGFybm9zIGNvbiB1bmEgYmFzZSBtw6FzIGbDoWNpbCBkZSB0cmFiYWphciwgeSBxdWUgY29udGllbmUgdW5pY2FtZW50ZSBsb3MgZGF0b3MgaW50ZXJlc2FudGVzLiBUYW1iacOpbiBlbGltaW5hbW9zIHByZXZpYW1lbnRlIGxvcyBvdXRsaWVycykKCmBgYHtyfQpkZiA8LSByZWFkX3JkcygnLi4vZnVlbnRlcy9kYXRvc19wcm9wZXJhdGkuUkRTJykKCmRmICU+JSBzYW1wbGVfbigxMCkKYGBgCgojIyMgUHJpbWVyIG1vZGVsbwoKYGBge3J9CmxtX2ZpdCA8LSBsbShwcmljZX4gbDMrIHJvb21zICsgYmF0aHJvb21zICsgc3VyZmFjZV90b3RhbCArIHByb3BlcnR5X3R5cGUsZGF0YSA9IGRmKQpgYGAKCmBgYHtyfQpzdW1tYXJ5KGxtX2ZpdCkKYGBgCgo+IMK/IFF1w6kgcGFzw7MgY29uIGxhcyB2YXJpYWJsZXMgbm8gbnVtw6lyaWNhcz8KPiDCv1NvbiBzaWduaWZpY2F0aXZvcyBsb3MgZXN0aW1hZG9yZXM/IMK/Y3VhbGVzPwo+IMK/Q8OzbW8gc2UgbGVlbiBsb3MgdmFsb3JlcyBkZSBsb3MgZXN0aW1hZG9yZXM/CgoKJCQKXERlbHRhIHkgPSBcYmV0YV8xIFxEZWx0YSB4CiQkCgojIyMgRmVhdHVyZSBlbmdpbmVlcmluZy4KClNlIGRlbm9taW5hIGluZ2VuaWVyw61hIGRlIGZlYXR1cmVzIGEgbGEgY29uc3RydWNjacOzbiBkZSBudWV2YXMgdmFyaWFibGVzIGEgcGFydGlyIGRlIGxhcyBvcmlnaW5hbGVzLiAKClBvciBlamVtcGxvLCBkYWRvIHF1ZSBtdWNob3MgZGUgbG9zIGJhcnJpb3Mgbm8gZXhwbGljYW4gc2lnbmlmaWNhdGl2YW1lbnRlIGxvcyBjYW1iaW9zIGVuIGxvcyBwcmVjaW9zLCBubyBlc3RhIGJ1ZW5vIGNvbnNlcnZhcmxvcyB0b2Rvcy4gQSBzdSB2ZXosIG5vIHNhYmVtb3MgcmVzcGVjdG8gYSBxdcOpIGJhcnJpbyBzZSBjb21wYXJhLgoKVW5hIHNvbHVjacOzbiBwdWVkZSBzZXIgYWdydXBhciBsb3MgYmFycmlvcyBlbiB0cmVzIGNhdGVnb3LDrWFzIHJlc3BlY3RvIGEgc3UgZWZlY3RvIGVuIGVsIHByZWNpbzogCgotIEFsdG8KLSBNZWRpbwotIEJham8KCkVuIHBhcnRpY3VsYXIsIHBvZGVtb3Mgbm90YXIgZGUgZXN0YSBwcmltZXJhIHJlZ3Jlc2nDs24gcXVlIGFsZ3Vub3MgYmFycmlvcyB0aWVuZW4gdW4gZWZlY3RvIHNpZ25pZmljYXRpdm8gZW4gc3ViaXIgZWwgdmFsb3IgZGUgbGEgcHJvcGllZGFkLCBjb21vIEJlbGdyYW5vIG8gUmVjb2xldGEuIAoKUGFyYSBjb25zdHJ1aXIgbGEgbnVldmEgdmFyaWFibGUsIHBvZGVtb3MgdmVyIGVsIHByZWNpbyBwcm9tZWRpbyBkZWwgbWV0cm8gY3VhZHJhZG8gcG9yIGJhcnJpbwoKYGBge3J9CgpkZl9iYXJyaW9zIDwtIGRmICU+JSAKICBncm91cF9ieShsMykgJT4lIAogIHN1bW1hcmlzZShwcmVjaW9fbTIgPSBtZWFuKHByaWNlL3N1cmZhY2VfdG90YWwpKSAKICAKZ2dwbG90KGRmX2JhcnJpb3MsYWVzKHByZWNpb19tMikpICsKICBnZW9tX2RvdHBsb3QobWV0aG9kID0gJ2hpc3RvZG90JywgYmlud2lkdGggPSAxMDApCgoKc3VtbWFyeShkZl9iYXJyaW9zJHByZWNpb19tMikKYGBgCgpDb24gZXN0ZSBncsOhZmljbyB2ZW1vcyBxdWUgcXVlIGhheSBtdWNob3MgYmFycmlvcyBjb24gdW4gcHJlY2lvIHByb21lZGlvIGNlcmNhbm8gYSAyNTAwIGTDs2xhcmVzIGVsICRtXjIkLgoKUG9kZW1vcyBkaXZpZHIgbG9zIHRyZXMgZ3J1cG9zIGFsIHJlZGVkb3IgZGUgbG9zIHF1YXJ0aWxlcyAxIHkgMy4gCgotIDwyMDAwIGJham8KLSAyMDAwLTI1MDAgbWVkaW8KLSBcPjI1MDAgYWx0bwoKYGBge3J9CmRmX2JhcnJpb3MgPC0gZGZfYmFycmlvcyAlPiUgCiAgbXV0YXRlKGJhcnJpbz0gY2FzZV93aGVuKHByZWNpb19tMjw9MjEwMCB+ICdiYWpvJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJlY2lvX20yPjIxMDAgJiBwcmVjaW9fbTI8MjQwMCB+ICdtZWRpbycsCiAgICAgICAgICAgICAgICAgICAgICAgICAgI2JldHdlZW4ocHJlY2lvX20yLDIxMDAsMjQwMCl+J21lZGlvJywgIyBPdHJhIGZvcm1hIGRlIGVzY3JpYmlybG8KICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJlY2lvX20yPj0yNDAwIH4gJ2FsdG8nKSkKCmRmX2JhcnJpb3MgJT4lIAogIHNhbXBsZV9uKDEwKQpgYGAKCkNvbiBlc3RhIG51ZXZhIHZhcmlhYmxlIHBvZGVtb3MgbW9kaWZpY2FyIGxhIHRhYmxhIG9yaWdpbmFsLgoKYGBge3J9CmRmIDwtIGRmICU+JSAKICBsZWZ0X2pvaW4oZGZfYmFycmlvcywgYnk9J2wzJykKYGBgCgoKeSB2b2x2ZW1vcyBhIGNhbGN1bGFyIGVsIG1vZGVsbwoKCgpgYGB7cn0KbG1fZml0IDwtIGxtKHByaWNlfiBiYXJyaW8rIHJvb21zICsgYmF0aHJvb21zICsgc3VyZmFjZV90b3RhbCArIHByb3BlcnR5X3R5cGUsZGF0YSA9IGRmKQoKc3VtbWFyeShsbV9maXQpCmBgYAoKU2kgcXVlcmVtb3MgcXVlIGNvbXBhcmUgY29udHJhICdiYXJyaW8gbWVkaW8nIHBvZGVtb3MgY29udmVydGlyIGxhIHZhcmlhYmxlIGVuIGZhY3RvciB5IGV4cGxpY2l0YXIgbG9zIG5pdmVsZXMKCmBgYHtyfQpkZiA8LSBkZiAlPiUgCiAgbXV0YXRlKGJhcnJpbyA9IGZhY3RvcihiYXJyaW8sIGxldmVscyA9IGMoJ21lZGlvJywgJ2FsdG8nLCdiYWpvJykpKQoKbG1fZml0IDwtIGxtKHByaWNlfiBiYXJyaW8rIHJvb21zICsgYmF0aHJvb21zICsgc3VyZmFjZV90b3RhbCArIHByb3BlcnR5X3R5cGUsZGF0YSA9IGRmKQoKc3VtbWFyeShsbV9maXQpCmBgYAoKCj4gQ3XDoWwgZXMgZWwgbWVqb3IgbW9kZWxvPyBDb24gbG9zIGJhcnJpb3MgY29sYXBzYWRvcyBlbiB0cmVzIG5pdmVsZXMsIG8gY29uIHRvZG9zIGxvcyBiYXJyaW9zPwoKCiMjIyBUcmFuc2Zvcm1hY2lvbmVzIGxvZy4gCgoKYGBge3J9CmRmICU+JSAKICBzYW1wbGVfbigxMDAwMCkgJT4lIApnZ3Bsb3QoLiwgYWVzKHN1cmZhY2VfdG90YWwsIHByaWNlKSkrCiAgZ2VvbV9wb2ludChhbHBoYT0wLjc1KSsKICBnZW9tX3Ntb290aChtZXRob2QgPSAnbG0nKQpgYGAKCk51ZXN0cm9zIGRhdG9zIG5vIHRpZW5lbiB2YXJpYW56YSBjb25zdGFudGUsIHRpZW5lbiBfaGV0ZXJvY2VkYXN0aWNpZGFkXy4gUXVlIGxhIHZhcmlhbnphIHNlYSBjb25zdGFudGUgZXMgdW5vIGRlIGxvcyBzdXBlc3RvcyBkZWwgbW9kZWxvIGxpbmVhbC4KCmBgYHtyfQpkZiAlPiUgCiAgc2FtcGxlX24oMTAwMDApICU+JSAKZ2dwbG90KC4sIGFlcyhzdXJmYWNlX3RvdGFsLCBwcmljZSkpKwogIGdlb21fcG9pbnQoYWxwaGE9MC43NSkrCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gJ2xtJykrCiAgc2NhbGVfeV9sb2cxMCgpKwogIHNjYWxlX3hfbG9nMTAoKQpgYGAKCkxhIHRyYW5zZm9ybWFjacOzbiBsb2dhcsOtdG1pY2EgcGFyZWNlIHNlciB1bmEgYnVlbmEgaWRlYSBwYXJhIG51ZXN0cm9zIGRhdG9zCgoKYGBge3J9CmxtX2ZpdCA8LSBsbShsb2cocHJpY2UpfiBiYXJyaW8rIHJvb21zICsgYmF0aHJvb21zICsgbG9nKHN1cmZhY2VfdG90YWwpICsgcHJvcGVydHlfdHlwZSxkYXRhID0gZGYpCgpzdW1tYXJ5KGxtX2ZpdCkKYGBgCgotIEVsICRSXjIkIHBhc8OzIGRlIDAuNjk3NyBhIDAuNzgzMyEhCgotIEPDs21vIHNlIGludGVycHJldGFuIGFob3JhIGxvcyBudWV2b3MgcGFyw6FtZXRyb3M/CgoKfCBNb2RlbG8gICAgICB8IFZhcmlhYmxlIERlcGVuZGllbnRlIHwgVmFyaWFibGUgSW5kZXBlbmRpZW50ZSB8IEludGVycHJldGFjacOzbiAkXGJldGFfMSQgICAgICB8CnwtLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18CnwgTml2ZWwtTml2ZWwgfCAkeSQgICAgICAgICAgICAgIHwgJHgkICAgICAgICAgICAgICAgIHwgJFxEZWx0YSB5ID0gXGJldGFfMSBcRGVsdGEgeCQgICAgICAgICB8CnwgTml2ZWwtbG9nICAgfCAkeSQgICAgICAgICAgICAgIHwgJGxvZyh4KSQgICAgICAgICAgIHwgJFxEZWx0YSB5ID0gKFxiZXRhXzEvMTAwKVwlIFxEZWx0YSB4JCB8CnwgTG9nLW5pdmVsICAgfCAkbG9nKHkpJCAgICAgICAgIHwgJHgkICAgICAgICAgICAgICAgIHwgJFwlIFxEZWx0YSB5ID0gKDEwMCBcYmV0YV8xKSBcRGVsdGEgeCR8CnwgTG9nLWxvZyAgICAgfCAkbG9nKHkpJCAgICAgICAgIHwgJGxvZyh4KSQgICAgICAgICAgIHwgJFwlIFxEZWx0YSB5ID0gXGJldGFfMSBcJSBcRGVsdGEgeCQgICB8CgojIyMgUHJlZGljY2lvbmVzCgpQYXJhIHByZWRlY2lyIHVuIG51ZXZvIGNhc28sIHBvZGVtb3MgY29uc3RydWlyIHVuIGRhdGFmcmFtZSBjb24gbGFzIHZhcmlhYmxlcy4gUG9yIGVqZW1wbG8KYGBge3J9CmNhc29fbnVldm8gPC0gdGliYmxlKGJhcnJpbz0nYWx0bycsCiAgICAgICByb29tcz0yLAogICAgICAgYmF0aHJvb21zPTIsCiAgICAgICBwcm9wZXJ0eV90eXBlPSdEZXBhcnRhbWVudG8nLAogICAgICAgc3VyZmFjZV90b3RhbD03OCkgCgpwcmVkaWN0KGxtX2ZpdCxuZXdkYXRhID0gY2Fzb19udWV2bykKYGBgCgoKCgpQZXJvIGRlYmVtb3MgcmVjb3JkYXIgcXVlIGVzdGUgZXMgZWwgdmFsb3IgZGVsIGxvZ2Fyw610bW8gZGVsIHBlcmNpby4gUG9yIGxvIHRhbnRvIHRlbmVtb3MgcXVlIHJlYWxpemFyIGVsIGNhbWluaW8gaW52ZXJzbyBjb24gbGEgZnVuY2nDs24gYGV4cGAKCgpgYGB7cn0KZXhwKHByZWRpY3QobG1fZml0LGNhc29fbnVldm8pKQpgYGAKCgojIyMgUGFyYSBzZWd1aXIgcHJhY3RpY2FuZG8KClVuIHByb2JsZW1hIGRlIGxvIHF1ZSB2aW1vcyBlbiBlc3RhIHByw6FjdGljYSBlcyBxdWUgbGFzIHNhbGlkYXMgZGUgYHN1bW1hcnkobG1fZml0KWAgZXMgdW5hIGltcHJlc2nDs24gZW4gbGEgY29uc29sYS4gRXMgbXV5IGRpZsOtY2lsIHNlZ3VpciB0cmFiYWphbmRvIGNvbiBlc29zIHJlc3VsdGFkb3MuIFBhcmEgcmVzb2x2ZXIgZXN0byBoYXkgdW4gcGFyIGRlIGxpYnJlcsOtYXMgcXVlIGluY29ycG9yYW4gZWwgbW9kZWxhZG8gbGluZWFsIGFsIGZsdWpvIGRlbCB0aWR5dmVyc2U6CgogICsgW0Jyb29tXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvYnJvb20vdmlnbmV0dGVzL2Jyb29tLmh0bWwpCiAgKyBbTW9kZWxyXShodHRwczovL21vZGVsci50aWR5dmVyc2Uub3JnLykKCg==