Introducción

Ahora que tenemos claros algunos aspectos relevantes del preprocesamiento de datos con tidyverse, podemos empezar a pensar en un análisis un poco más elaborado. Para ello, vamos a usar nuestro viejo y conocido dataset de delitos pero lo vamos a combinar con información censal correspondiente a los radios de la CABA.

Objetivos

  • Introducir nociones generales de modelos lineales
  • Presentar las funciones en R para estimar y evaluar dichos modelos

Preprocesando el dataset

Vamos a combinar información de los radios censales con nuestro dataset de delitos. La pregunta general que vamos a abordar es ¿qué características tienen los radios censales en los que se cometen delitos en la CABA?

Primero, carguemos nuestro dataset de delitos, filtremos los datos sin información de latitud y longitud y demos el formato adecuado a las varaibles de fecha y hora.

delitos <- read.csv('../data/delitos.csv')
delitos <- delitos %>% 
        filter(latitud!=0 | longitud!=0) %>%
        mutate(fecha=ymd(fecha),
               hora=hms(hora))

Bien, ahora traigamos un datasets con información acerca de los radios censales de la ciudad.

radios_gral <- st_read('../data/radios_info_gral.geojson')
Reading layer `radios_info_gral' from data source `F:\PEN\KINGSTON\PEN2\Cursos\REPO_Tidy_Curso_UNSE\data\radios_info_gral.geojson' using driver `GeoJSON'
Simple feature collection with 3554 features and 27 fields
geometry type:  MULTIPOLYGON
dimension:      XY
bbox:           xmin: -58.53092 ymin: -34.70574 xmax: -58.33455 ymax: -34.528
epsg (SRID):    4326
proj4string:    +proj=longlat +datum=WGS84 +no_defs
str(radios_gral)
Classes ‘sf’ and 'data.frame':  3554 obs. of  28 variables:
 $ RADIO                   : Factor w/ 3554 levels "020010101","020010201",..: 1 139 148 149 140 141 142 143 144 145 ...
 $ RADIO_ID                : Factor w/ 3554 levels "1_1_1","1_10_1",..: 1 30 31 32 34 35 36 37 38 39 ...
 $ BARRIO                  : Factor w/ 48 levels "AGRONOMIA","ALMAGRO",..: 29 32 32 32 32 32 32 32 32 32 ...
 $ COMUNA                  : Factor w/ 15 levels "1","10","11",..: 1 1 1 1 1 1 1 1 1 1 ...
 $ POBLACION               : num  336 341 296 528 229 723 393 600 472 786 ...
 $ VIVIENDAS               : num  82 365 629 375 445 744 341 505 504 546 ...
 $ HOGARES                 : num  65 116 101 136 129 314 209 275 202 347 ...
 $ HOGARES_NBI             : num  19 25 1 7 16 104 110 32 49 89 ...
 $ AREA_KM2                : num  1.799 0.0186 0.0444 0.3663 0.0184 ...
 $ tasa_desoc              : num  3.64 5.71 1.36 2.08 4.99 ...
 $ tasa_act                : num  85.2 75.2 88.1 81.6 84.2 ...
 $ razon_masc              : num  1.71 1.174 1.208 1.257 0.789 ...
 $ prop_mayores            : num  0.00982 0.03871 0.03547 0.02254 0.04585 ...
 $ tasa_cal_construct_insuf: num  26.3 8.8 3.1 4.5 3.1 ...
 $ tasa_cal_serv_insuf     : num  7.9 3.5 3.1 3 3.1 7.1 9.6 0.4 7.1 9.4 ...
 $ tasa_cal_mat_insuf      : num  13.2 0 1 3.8 0 ...
 $ dist_bancos             : num  166.3 116 68.3 25 128.1 ...
 $ dist_seguridad          : num  667 483 497 448 576 ...
 $ dist_tren               : num  556 1660 926 543 1530 ...
 $ dist_subte              : num  695 219 250 316 304 ...
 $ dist_colectivo          : num  27.7 77 89.9 115.6 37 ...
 $ dist_metrobus           : num  949 338 430 178 211 ...
 $ n_dptos                 : num  3 23 37 37 18 16 26 53 54 52 ...
 $ mean_USS_M2             : num  3005 1566 1902 1832 1950 ...
 $ std_USS_M2              : num  1007 347 415 543 307 ...
 $ median_USS_M2           : num  3407 1500 1915 1846 1986 ...
 $ mad_USS2_M2             : num  509 316 430 443 188 ...
 $ geometry                :sfc_MULTIPOLYGON of length 3554; first list element: List of 1
  ..$ :List of 1
  .. ..$ : num [1:409, 1:2] -58.4 -58.4 -58.4 -58.4 -58.4 ...
  ..- attr(*, "class")= chr  "XY" "MULTIPOLYGON" "sfg"
 - attr(*, "sf_column")= chr "geometry"
 - attr(*, "agr")= Factor w/ 3 levels "constant","aggregate",..: NA NA NA NA NA NA NA NA NA NA ...
  ..- attr(*, "names")= chr  "RADIO" "RADIO_ID" "BARRIO" "COMUNA" ...

Ahora bien, este dataset es de un tipo particular. Si se fijan el último campo se llama geometry y contiene la información para plotear los politicos de los radios.

ggplot() + geom_sf(data = radios_gral)

Para poder operar bien con los datasets conjuntos, tenemos que transformar los datos de delitos en un formato geográfico. Para ello usamos la librería sf.

delitos <- st_as_sf(delitos, 
                    coords=c('longitud', 'latitud'), 
                    crs=4326)

Básicamente, le pasamos a la función st_as_sf las columnas que contienen la información geográfica de coordenadas (coords) y la proyección (crs) en la que están los datos.

Ahora, vamos a hacer las cosas interesantes. La idea es poder realizar una operación que se llama “join espacial”. Vamos a tratar de contar cuántos delitos hubo en cada uno de los radios censales.

ggplot() + 
        geom_sf(data=radios_gral) + 
        geom_sf(data=sample_n(delitos,1000), aes(color=tipo_delito)) + 
        facet_wrap(~tipo_delito)

La operación va a constar de dos partes: la primera, vemos a joinear los datos de delitos con los los de radio. Generamos una nuevo objeto llamado delitos_radios que contiene para cada delito, la información del tadio al que pertenece.

Usamos la función st_join que sirve par realizar estas operaciones. Pasamos ambos objetos como argumentos y usamos el argumento st_within.

delitos_radio <- st_join(delitos, 
                          radios_gral, 
                          join = st_within) %>%
        as_tibble() 
although coordinates are longitude/latitude, st_within assumes that they are planar
head(delitos)
Simple feature collection with 6 features and 12 fields
geometry type:  POINT
dimension:      XY
bbox:           xmin: -58.5193 ymin: -34.6524 xmax: -58.4053 ymax: -34.5615
epsg (SRID):    4326
proj4string:    +proj=longlat +datum=WGS84 +no_defs
      id    comuna           barrio      fecha      hora
1  68400  Comuna 3        BALVANERA 2016-10-31  1H 0M 0S
2  68401  Comuna 7           FLORES 2016-10-31 2H 30M 0S
3  68402  Comuna 4    NUEVA POMPEYA 2016-10-31  4H 0M 0S
4  68492 Comuna 13         BELGRANO 2016-10-31 2H 58M 0S
5 132437  Comuna 9          LINIERS 2016-10-31 21H 0M 0S
6 132469 Comuna 11 VILLA SANTA RITA 2016-10-31  8H 0M 0S
         uso_arma uso_moto lugar origen_dato           tipo_delito
1 SIN USO DE ARMA SIN MOTO    NA          NA      Homicidio Doloso
2 SIN USO DE ARMA SIN MOTO    NA          NA      Homicidio Doloso
3 SIN USO DE ARMA SIN MOTO    NA          NA      Homicidio Doloso
4 SIN USO DE ARMA SIN MOTO    NA          NA    Homicidio Seg Vial
5 SIN USO DE ARMA SIN MOTO    NA          NA Hurto (Sin violencia)
6 SIN USO DE ARMA SIN MOTO    NA          NA Hurto (Sin violencia)
  cantidad_vehiculos cantidad_victimas                  geometry
1                  0                 0 POINT (-58.4117 -34.6141)
2                  0                 0 POINT (-58.4547 -34.6509)
3                  0                 0 POINT (-58.4053 -34.6499)
4                  0                 1 POINT (-58.4557 -34.5615)
5                  0                 0 POINT (-58.5193 -34.6524)
6                  0                 0 POINT (-58.4833 -34.6218)

Luego, contamos la cantidad de puntos en cada radio con un simple group_by + summarise0 y realizamos un left_join con la tabla original de radios.

radios_delitos <- delitos_radio %>%
        group_by(RADIO_ID) %>%
        summarise(n_delitos=n()) %>%
        left_join(x=radios_gral)
Factor `RADIO_ID` contains implicit NA, consider using `forcats::fct_explicit_na`Joining, by = "RADIO_ID"

Vemos, ahora, que el objeto radios_delitos es igual a radios_general pero con una columna más que se llama n_delitos

str(radios_delitos)
Classes ‘sf’ and 'data.frame':  3554 obs. of  29 variables:
 $ RADIO                   : Factor w/ 3554 levels "020010101","020010201",..: 1 139 148 149 140 141 142 143 144 145 ...
 $ RADIO_ID                : Factor w/ 3554 levels "1_1_1","1_10_1",..: 1 30 31 32 34 35 36 37 38 39 ...
 $ BARRIO                  : Factor w/ 48 levels "AGRONOMIA","ALMAGRO",..: 29 32 32 32 32 32 32 32 32 32 ...
 $ COMUNA                  : Factor w/ 15 levels "1","10","11",..: 1 1 1 1 1 1 1 1 1 1 ...
 $ POBLACION               : num  336 341 296 528 229 723 393 600 472 786 ...
 $ VIVIENDAS               : num  82 365 629 375 445 744 341 505 504 546 ...
 $ HOGARES                 : num  65 116 101 136 129 314 209 275 202 347 ...
 $ HOGARES_NBI             : num  19 25 1 7 16 104 110 32 49 89 ...
 $ AREA_KM2                : num  1.799 0.0186 0.0444 0.3663 0.0184 ...
 $ tasa_desoc              : num  3.64 5.71 1.36 2.08 4.99 ...
 $ tasa_act                : num  85.2 75.2 88.1 81.6 84.2 ...
 $ razon_masc              : num  1.71 1.174 1.208 1.257 0.789 ...
 $ prop_mayores            : num  0.00982 0.03871 0.03547 0.02254 0.04585 ...
 $ tasa_cal_construct_insuf: num  26.3 8.8 3.1 4.5 3.1 ...
 $ tasa_cal_serv_insuf     : num  7.9 3.5 3.1 3 3.1 7.1 9.6 0.4 7.1 9.4 ...
 $ tasa_cal_mat_insuf      : num  13.2 0 1 3.8 0 ...
 $ dist_bancos             : num  166.3 116 68.3 25 128.1 ...
 $ dist_seguridad          : num  667 483 497 448 576 ...
 $ dist_tren               : num  556 1660 926 543 1530 ...
 $ dist_subte              : num  695 219 250 316 304 ...
 $ dist_colectivo          : num  27.7 77 89.9 115.6 37 ...
 $ dist_metrobus           : num  949 338 430 178 211 ...
 $ n_dptos                 : num  3 23 37 37 18 16 26 53 54 52 ...
 $ mean_USS_M2             : num  3005 1566 1902 1832 1950 ...
 $ std_USS_M2              : num  1007 347 415 543 307 ...
 $ median_USS_M2           : num  3407 1500 1915 1846 1986 ...
 $ mad_USS2_M2             : num  509 316 430 443 188 ...
 $ n_delitos               : int  841 35 190 1135 29 203 25 65 78 139 ...
 $ geometry                :sfc_MULTIPOLYGON of length 3554; first list element: List of 1
  ..$ :List of 1
  .. ..$ : num [1:409, 1:2] -58.4 -58.4 -58.4 -58.4 -58.4 ...
  ..- attr(*, "class")= chr  "XY" "MULTIPOLYGON" "sfg"
 - attr(*, "sf_column")= chr "geometry"
 - attr(*, "agr")= Factor w/ 3 levels "constant","aggregate",..: NA NA NA NA NA NA NA NA NA NA ...
  ..- attr(*, "names")= chr  "RADIO" "RADIO_ID" "BARRIO" "COMUNA" ...

Hagamos un primer mapa a ver qué pasa…

ggplot() +
        geom_sf(data=radios_delitos, 
                color=NA,
                aes(fill=n_delitos)) + 
        scale_fill_viridis_c() + 
        theme_minimal()

Ahora bien, pareciera que los radios con mayor cantidad de delitos son los que se encuentran en el centro de la ciudad o en zonas de alta población. Por ello, creemos una medida de “delitos / habitantes” para controlar este factor y aprovechemos para calcular una variable de densidad poblacional:

radios_delitos <- radios_delitos %>%
        mutate(n_delitos_hab= n_delitos / POBLACION,
               dens_pob=POBLACION/AREA_KM2)

Y veamos ahora el plot…

ggplot() +
        geom_sf(data=radios_delitos, 
                color=NA,
                aes(fill=n_delitos_hab)) + 
        scale_fill_viridis_c() + 
        theme_minimal()

Ahora la cosa parece bastante más homgénea… pero porque tenemos un caso muy raro. Vamos a truncar los valores a un máximo

radios_delitos <- radios_delitos %>%
        mutate(n_delitos_hab = ifelse(n_delitos_hab >=0.6, 0.6, n_delitos_hab))
radios_delitos %>%
ggplot() +
        geom_sf( 
                color=NA,
                aes(fill=n_delitos_hab)) + 
        scale_fill_viridis_c() + 
        theme_minimal()

Consigna

  • Agregar al objeto radios_delitos columnas que cuenten para cada radio cada uno de los tipos de delitos en el objeto `delito. Nombrar las variables resultantes de la siguiente forma:

  • hom_doloso
  • hom_segvial
  • hurto_sv
  • hurto_auto
  • les_segvial
  • robo_v
  • robo_auto

radios_delitos <- delitos_radio %>%
        group_by(RADIO_ID, tipo_delito) %>%
        summarize(n=n()) %>%
        spread(key = tipo_delito, value = n) %>%
        left_join(x=radios_delitos) %>%
        replace(., is.na(.),0) %>%
        rename(hom_doloso='Homicidio Doloso',
               hom_segvial='Homicidio Seg Vial',
               robo_noviol='Hurto (Sin violencia)',
               hurto_auto = 'Hurto Automotor',
               les_segvial='Lesiones Seg Vial',
               robo_viol='Robo (Con violencia)',
               robo_auto='Robo Automotor'
               )
Factor `RADIO_ID` contains implicit NA, consider using `forcats::fct_explicit_na`Factor `RADIO_ID` contains implicit NA, consider using `forcats::fct_explicit_na`Factor `RADIO_ID` contains implicit NA, consider using `forcats::fct_explicit_na`Factor `RADIO_ID` contains implicit NA, consider using `forcats::fct_explicit_na`Factor `RADIO_ID` contains implicit NA, consider using `forcats::fct_explicit_na`Joining, by = "RADIO_ID"

Y ordenamos un poco el dataset…

radios_delitos<- radios_delitos %>%
        mutate(homicidio = hom_doloso + hom_segvial,
               robo_automovil = hurto_auto + robo_auto) %>%
        select(-hom_doloso, -hom_segvial, -hurto_auto, -robo_auto)
        
  • Generemos una variable con la proporción de Hogares NBI
radios_delitos <- radios_delitos %>% mutate(p_hognbi = HOGARES_NBI/HOGARES * 
    100) %>% replace_na(list(p_hognbi = 0))

Introducción a la regresión lineal en R

Todos nos acordamos del modelo lineal:

\[Y_{i}=\beta_{0} + \beta_{1}*X_{i} + \epsilon_{i}\]

\(\beta_{0}\) es el intercepto

\(\beta_{1}\) es la pendiente de la variable \(X\); es decir el efecto medio en \(Y\) cuando \(X\) se incrementa en una unidad (y todo lo demás, se mantiene constante)

\(\epsilon_{i}\) es el error o residuo de estimación

En un modelo lineal buscamos “minimizar” una determinada métrica de error. En particular, buscamos hacer mínimo el error cuadrático medio (MSE):

\[MSE=\frac{1}{n}\sum_{i=1}^{n}(Y_{i}-\hat{Y_{i}})^2\]

Implementación en R lm()

Para implementar en R una regresión lineal simple usamos la función lm() * formula: una expresión con la siguiente forma: y~x * data: dataframe o datamatrix a utilizar * subset: un vector que define un subconjunto de datos a usar en el modelo * weights: vector que define pesos para la regresión (WLNS)

model <- lm(formula = n_delitos ~ AREA_KM2, data = radios_delitos)

Si imprimimos el modelo… solamente nos da una información básica: el intercepto y el valor de la pendiente.

summary(model)

Call:
lm(formula = n_delitos ~ AREA_KM2, data = radios_delitos)

Residuals:
    Min      1Q  Median      3Q     Max 
-560.78  -31.18  -13.88    9.20 1490.21 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)   57.798      1.319   43.83   <2e-16 ***
AREA_KM2     153.763      8.138   18.90   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 73.52 on 3552 degrees of freedom
Multiple R-squared:  0.09134,   Adjusted R-squared:  0.09108 
F-statistic:   357 on 1 and 3552 DF,  p-value: < 2.2e-16

Ahora tenemos acceso a mucha más información: * p-valores y errores estándar de los coeficientes. ¿Son significativos? * \(R^2\): menos del 10% de la variancia de la variable dependiente es explicada por el modelo

Veamos qué hay dentro del objeto model. Usamos la función names para acceder a los objetos dentro del objeto model. Luego, podemos ir usando los nombres para acceder a los diferentes elementos.

names(model)
 [1] "coefficients"  "residuals"     "effects"       "rank"         
 [5] "fitted.values" "assign"        "qr"            "df.residual"  
 [9] "xlevels"       "call"          "terms"         "model"        
model$coefficients
(Intercept)    AREA_KM2 
   57.79752   153.76348 

Implementación en R lm(): Algunas funciones útiles:

Obtenemos intervalos de confianza de los parámetros del modelo.

coef(model)
(Intercept)    AREA_KM2 
   57.79752   153.76348 
confint(model, level = 0.95)
                2.5 %    97.5 %
(Intercept)  55.21212  60.38293
AREA_KM2    137.80871 169.71825

Veamos la función predict()

predict(model, data.frame(AREA_KM2 = c(1, 10, 100), interval = "prediction"))
        1         2         3 
  211.561  1595.432 15434.145 

Es decir, que para si la variable independiente AREA_KM2 presentara los valores 1, 10 y 100, la variable dependiente n_delitos presentaría esos valores (en la media).

Grafiquemos, ahora, todo.

ggplot(data = radios_delitos, aes(x = AREA_KM2, y = n_delitos)) + geom_point() + 
    geom_smooth(method = "lm")

Generemos un modelo, ahora, que contenga todas las variables del dataset. Veamos, primero, la correlación entre varias variables:

select(as.data.frame(radios_delitos), -c(RADIO:HOGARES_NBI, geometry)) %>% cor(method = "pearson")
                             AREA_KM2   tasa_desoc     tasa_act
AREA_KM2                  1.000000000  0.027929960 -0.123767578
tasa_desoc                0.027929960  1.000000000 -0.175775953
tasa_act                 -0.123767578 -0.175775953  1.000000000
razon_masc                0.276496395  0.036547152  0.141251592
prop_mayores              0.119987794 -0.209828293 -0.096635952
tasa_cal_construct_insuf  0.144959175  0.329082059  0.127115347
tasa_cal_serv_insuf       0.323568913  0.191255031  0.109181667
tasa_cal_mat_insuf        0.161713836  0.389195119 -0.008936087
dist_bancos               0.217646563  0.334244539 -0.158830649
dist_seguridad            0.142870189  0.144722554 -0.193536563
dist_tren                 0.020277722  0.151783598 -0.109821793
dist_subte                0.166485179  0.260388444 -0.378866108
dist_colectivo            0.219082565  0.202263563 -0.046518152
dist_metrobus             0.031158450  0.039352461 -0.071130985
n_dptos                   0.030821255 -0.273652426  0.250985597
mean_USS_M2              -0.044958753 -0.466959134  0.204149782
std_USS_M2               -0.008044273 -0.269962819  0.115672927
median_USS_M2            -0.039769360 -0.464856004  0.203458670
mad_USS2_M2              -0.037599804 -0.332538866  0.154194499
n_delitos                 0.302220317  0.009591662  0.045295303
n_delitos_hab             0.287940899 -0.004361832  0.069389914
dens_pob                 -0.260986962 -0.038171640  0.115627408
robo_noviol               0.170691470 -0.042288299  0.081846436
les_segvial               0.427982641  0.007297648 -0.022412288
robo_viol                 0.321216185  0.029749002  0.043867423
homicidio                 0.202971902  0.082264251 -0.013118552
robo_automovil            0.246030607  0.146756042 -0.199129304
p_hognbi                  0.115102809  0.286410983  0.225893960
                          razon_masc prop_mayores
AREA_KM2                  0.27649640   0.11998779
tasa_desoc                0.03654715  -0.20982829
tasa_act                  0.14125159  -0.09663595
razon_masc                1.00000000  -0.05310139
prop_mayores             -0.05310139   1.00000000
tasa_cal_construct_insuf  0.21380058  -0.25504287
tasa_cal_serv_insuf       0.33754039  -0.16043125
tasa_cal_mat_insuf        0.23175266  -0.27080311
dist_bancos               0.15095304  -0.22491240
dist_seguridad            0.06650783  -0.09109948
dist_tren                 0.02523499  -0.08494967
dist_subte                0.09456318  -0.12483040
dist_colectivo            0.09647359  -0.17924918
dist_metrobus             0.01294844  -0.07200218
n_dptos                  -0.04227342   0.14320211
mean_USS_M2              -0.12324280   0.22350570
std_USS_M2               -0.05752163   0.11652906
median_USS_M2            -0.12126626   0.22231112
mad_USS2_M2              -0.07848542   0.13536479
n_delitos                 0.06358147   0.15858903
n_delitos_hab             0.11784038   0.25553020
dens_pob                 -0.11370188  -0.08862508
robo_noviol               0.04566822   0.13984635
les_segvial               0.07639771   0.23053472
robo_viol                 0.06141995   0.15535867
homicidio                 0.07642447  -0.02471472
robo_automovil            0.04529027  -0.06589268
p_hognbi                  0.24660825  -0.21500565
                         tasa_cal_construct_insuf tasa_cal_serv_insuf
AREA_KM2                              0.144959175          0.32356891
tasa_desoc                            0.329082059          0.19125503
tasa_act                              0.127115347          0.10918167
razon_masc                            0.213800580          0.33754039
prop_mayores                         -0.255042871         -0.16043125
tasa_cal_construct_insuf              1.000000000          0.72222895
tasa_cal_serv_insuf                   0.722228952          1.00000000
tasa_cal_mat_insuf                    0.759278997          0.54616275
dist_bancos                           0.264475262          0.20060109
dist_seguridad                        0.061234071          0.03806261
dist_tren                             0.027530442          0.02360199
dist_subte                            0.023819233          0.04376286
dist_colectivo                        0.390826322          0.26611914
dist_metrobus                         0.005779055          0.03608026
n_dptos                              -0.167896964         -0.12410641
mean_USS_M2                          -0.446137125         -0.26797529
std_USS_M2                           -0.247856021         -0.16252763
median_USS_M2                        -0.447301607         -0.26389339
mad_USS2_M2                          -0.294476128         -0.19946100
n_delitos                             0.012549853          0.04791069
n_delitos_hab                         0.001901648          0.05580751
dens_pob                              0.176266463          0.02155561
robo_noviol                          -0.019290938          0.01798743
les_segvial                           0.012098292          0.03846623
robo_viol                             0.043951809          0.06878106
homicidio                             0.158611676          0.11642789
robo_automovil                       -0.067234405         -0.02414993
p_hognbi                              0.767819201          0.59680200
                         tasa_cal_mat_insuf dist_bancos dist_seguridad
AREA_KM2                        0.161713836  0.21764656     0.14287019
tasa_desoc                      0.389195119  0.33424454     0.14472255
tasa_act                       -0.008936087 -0.15883065    -0.19353656
razon_masc                      0.231752663  0.15095304     0.06650783
prop_mayores                   -0.270803114 -0.22491240    -0.09109948
tasa_cal_construct_insuf        0.759278997  0.26447526     0.06123407
tasa_cal_serv_insuf             0.546162752  0.20060109     0.03806261
tasa_cal_mat_insuf              1.000000000  0.37018520     0.14589287
dist_bancos                     0.370185198  1.00000000     0.29540865
dist_seguridad                  0.145892866  0.29540865     1.00000000
dist_tren                       0.068538208  0.11388267     0.14482392
dist_subte                      0.140947913  0.39758873     0.40045880
dist_colectivo                  0.434325848  0.29129806     0.17397556
dist_metrobus                   0.064088591  0.12323693     0.09917295
n_dptos                        -0.216749528 -0.27792844    -0.17741827
mean_USS_M2                    -0.478993059 -0.44918567    -0.20783678
std_USS_M2                     -0.273039136 -0.24341069    -0.11476380
median_USS_M2                  -0.481806989 -0.44983751    -0.20390430
mad_USS2_M2                    -0.324531768 -0.30560324    -0.13124273
n_delitos                      -0.017380602 -0.04290772    -0.03821480
n_delitos_hab                  -0.043701697 -0.07570274    -0.08707410
dens_pob                        0.081263156 -0.22403771    -0.20187778
robo_noviol                    -0.049907162 -0.11553141    -0.07997306
les_segvial                    -0.015177643 -0.01910650     0.01853159
robo_viol                       0.011115293 -0.01672521    -0.03643620
homicidio                       0.139939854  0.14984763     0.08010512
robo_automovil                 -0.013563115  0.25010820     0.22236209
p_hognbi                        0.492445267  0.17740175    -0.02698953
                            dist_tren   dist_subte dist_colectivo
AREA_KM2                  0.020277722  0.166485179    0.219082565
tasa_desoc                0.151783598  0.260388444    0.202263563
tasa_act                 -0.109821793 -0.378866108   -0.046518152
razon_masc                0.025234987  0.094563183    0.096473592
prop_mayores             -0.084949670 -0.124830398   -0.179249183
tasa_cal_construct_insuf  0.027530442  0.023819233    0.390826322
tasa_cal_serv_insuf       0.023601985  0.043762863    0.266119138
tasa_cal_mat_insuf        0.068538208  0.140947913    0.434325848
dist_bancos               0.113882675  0.397588726    0.291298060
dist_seguridad            0.144823922  0.400458803    0.173975557
dist_tren                 1.000000000  0.147849184   -0.013704500
dist_subte                0.147849184  1.000000000    0.167935683
dist_colectivo           -0.013704500  0.167935683    1.000000000
dist_metrobus             0.289720678  0.053291442   -0.009556626
n_dptos                  -0.175905091 -0.307276113   -0.146188016
mean_USS_M2              -0.185267729 -0.333400738   -0.222308742
std_USS_M2               -0.130581983 -0.188230212   -0.129450258
median_USS_M2            -0.180979184 -0.325806547   -0.226073182
mad_USS2_M2              -0.163447630 -0.219096231   -0.159486732
n_delitos                -0.021900729 -0.002464865   -0.098707104
n_delitos_hab            -0.038535042 -0.047306835   -0.137455289
dens_pob                 -0.003168642 -0.362957007    0.017963188
robo_noviol              -0.041829773 -0.056822499   -0.089056982
les_segvial              -0.005484867  0.029891975   -0.111486464
robo_viol                -0.028854057 -0.015338990   -0.102017183
homicidio                 0.020139621  0.076064452    0.059690329
robo_automovil            0.176503225  0.389629387    0.063669247
p_hognbi                  0.048263132 -0.065700094    0.166885950
                         dist_metrobus     n_dptos  mean_USS_M2
AREA_KM2                   0.031158450  0.03082126 -0.044958753
tasa_desoc                 0.039352461 -0.27365243 -0.466959134
tasa_act                  -0.071130985  0.25098560  0.204149782
razon_masc                 0.012948440 -0.04227342 -0.123242796
prop_mayores              -0.072002178  0.14320211  0.223505700
tasa_cal_construct_insuf   0.005779055 -0.16789696 -0.446137125
tasa_cal_serv_insuf        0.036080256 -0.12410641 -0.267975289
tasa_cal_mat_insuf         0.064088591 -0.21674953 -0.478993059
dist_bancos                0.123236926 -0.27792844 -0.449185667
dist_seguridad             0.099172945 -0.17741827 -0.207836783
dist_tren                  0.289720678 -0.17590509 -0.185267729
dist_subte                 0.053291442 -0.30727611 -0.333400738
dist_colectivo            -0.009556626 -0.14618802 -0.222308742
dist_metrobus              1.000000000 -0.02996514 -0.071134350
n_dptos                   -0.029965140  1.00000000  0.446246347
mean_USS_M2               -0.071134350  0.44624635  1.000000000
std_USS_M2                -0.065937095  0.31662526  0.662726067
median_USS_M2             -0.063740396  0.43846883  0.985726081
mad_USS2_M2               -0.068254274  0.34750695  0.700931500
n_delitos                 -0.060904333  0.19216451 -0.028377676
n_delitos_hab             -0.088885535  0.15296767 -0.007249579
dens_pob                   0.047450491  0.04083969  0.073738048
robo_noviol               -0.071224595  0.17010730  0.046455155
les_segvial               -0.025323272  0.20840225 -0.008285561
robo_viol                 -0.049596923  0.19827624 -0.051926129
homicidio                 -0.007109436 -0.02471306 -0.150059683
robo_automovil             0.010838960 -0.10478746 -0.247695940
p_hognbi                  -0.030416512 -0.13727293 -0.384665934
                           std_USS_M2 median_USS_M2 mad_USS2_M2
AREA_KM2                 -0.008044273  -0.039769360 -0.03759980
tasa_desoc               -0.269962819  -0.464856004 -0.33253887
tasa_act                  0.115672927   0.203458670  0.15419450
razon_masc               -0.057521630  -0.121266256 -0.07848542
prop_mayores              0.116529059   0.222311116  0.13536479
tasa_cal_construct_insuf -0.247856021  -0.447301607 -0.29447613
tasa_cal_serv_insuf      -0.162527626  -0.263893388 -0.19946100
tasa_cal_mat_insuf       -0.273039136  -0.481806989 -0.32453177
dist_bancos              -0.243410694  -0.449837512 -0.30560324
dist_seguridad           -0.114763802  -0.203904304 -0.13124273
dist_tren                -0.130581983  -0.180979184 -0.16344763
dist_subte               -0.188230212  -0.325806547 -0.21909623
dist_colectivo           -0.129450258  -0.226073182 -0.15948673
dist_metrobus            -0.065937095  -0.063740396 -0.06825427
n_dptos                   0.316625259   0.438468832  0.34750695
mean_USS_M2               0.662726067   0.985726081  0.70093150
std_USS_M2                1.000000000   0.578552744  0.68405530
median_USS_M2             0.578552744   1.000000000  0.68380561
mad_USS2_M2               0.684055296   0.683805606  1.00000000
n_delitos                 0.023090389  -0.029792163  0.02259867
n_delitos_hab             0.034509774  -0.009510193  0.03566651
dens_pob                 -0.010987356   0.069853853  0.00289545
robo_noviol               0.057052854   0.044598139  0.06684448
les_segvial               0.028908828  -0.008618662  0.03272413
robo_viol                 0.009601675  -0.053421311  0.00521352
homicidio                -0.084863813  -0.150152344 -0.09987613
robo_automovil           -0.121862510  -0.243915911 -0.15601691
p_hognbi                 -0.194230137  -0.385824101 -0.24380564
                            n_delitos n_delitos_hab     dens_pob
AREA_KM2                  0.302220317   0.287940899 -0.260986962
tasa_desoc                0.009591662  -0.004361832 -0.038171640
tasa_act                  0.045295303   0.069389914  0.115627408
razon_masc                0.063581473   0.117840377 -0.113701884
prop_mayores              0.158589031   0.255530201 -0.088625077
tasa_cal_construct_insuf  0.012549853   0.001901648  0.176266463
tasa_cal_serv_insuf       0.047910692   0.055807513  0.021555614
tasa_cal_mat_insuf       -0.017380602  -0.043701697  0.081263156
dist_bancos              -0.042907720  -0.075702741 -0.224037713
dist_seguridad           -0.038214798  -0.087074101 -0.201877776
dist_tren                -0.021900729  -0.038535042 -0.003168642
dist_subte               -0.002464865  -0.047306835 -0.362957007
dist_colectivo           -0.098707104  -0.137455289  0.017963188
dist_metrobus            -0.060904333  -0.088885535  0.047450491
n_dptos                   0.192164506   0.152967673  0.040839687
mean_USS_M2              -0.028377676  -0.007249579  0.073738048
std_USS_M2                0.023090389   0.034509774 -0.010987356
median_USS_M2            -0.029792163  -0.009510193  0.069853853
mad_USS2_M2               0.022598668   0.035666513  0.002895450
n_delitos                 1.000000000   0.805769139 -0.300564460
n_delitos_hab             0.805769139   1.000000000 -0.363300015
dens_pob                 -0.300564460  -0.363300015  1.000000000
robo_noviol               0.892304087   0.717159349 -0.206740053
les_segvial               0.682972321   0.570716446 -0.262875236
robo_viol                 0.947801231   0.765048072 -0.283714443
homicidio                 0.237653130   0.204823348 -0.061712843
robo_automovil            0.289733127   0.218041950 -0.420887511
p_hognbi                  0.077950779   0.102026115  0.056174542
                         robo_noviol  les_segvial    robo_viol
AREA_KM2                  0.17069147  0.427982641  0.321216185
tasa_desoc               -0.04228830  0.007297648  0.029749002
tasa_act                  0.08184644 -0.022412288  0.043867423
razon_masc                0.04566822  0.076397713  0.061419953
prop_mayores              0.13984635  0.230534723  0.155358665
tasa_cal_construct_insuf -0.01929094  0.012098292  0.043951809
tasa_cal_serv_insuf       0.01798743  0.038466234  0.068781061
tasa_cal_mat_insuf       -0.04990716 -0.015177643  0.011115293
dist_bancos              -0.11553141 -0.019106499 -0.016725209
dist_seguridad           -0.07997306  0.018531591 -0.036436197
dist_tren                -0.04182977 -0.005484867 -0.028854057
dist_subte               -0.05682250  0.029891975 -0.015338990
dist_colectivo           -0.08905698 -0.111486464 -0.102017183
dist_metrobus            -0.07122460 -0.025323272 -0.049596923
n_dptos                   0.17010730  0.208402247  0.198276239
mean_USS_M2               0.04645516 -0.008285561 -0.051926129
std_USS_M2                0.05705285  0.028908828  0.009601675
median_USS_M2             0.04459814 -0.008618662 -0.053421311
mad_USS2_M2               0.06684448  0.032724130  0.005213520
n_delitos                 0.89230409  0.682972321  0.947801231
n_delitos_hab             0.71715935  0.570716446  0.765048072
dens_pob                 -0.20674005 -0.262875236 -0.283714443
robo_noviol               1.00000000  0.488508086  0.714868154
les_segvial               0.48850809  1.000000000  0.687799110
robo_viol                 0.71486815  0.687799110  1.000000000
homicidio                 0.12314672  0.237002218  0.268733676
robo_automovil            0.09785313  0.218715730  0.277668830
p_hognbi                  0.02798243  0.053096318  0.112999829
                            homicidio robo_automovil    p_hognbi
AREA_KM2                  0.202971902     0.24603061  0.11510281
tasa_desoc                0.082264251     0.14675604  0.28641098
tasa_act                 -0.013118552    -0.19912930  0.22589396
razon_masc                0.076424471     0.04529027  0.24660825
prop_mayores             -0.024714724    -0.06589268 -0.21500565
tasa_cal_construct_insuf  0.158611676    -0.06723441  0.76781920
tasa_cal_serv_insuf       0.116427891    -0.02414993  0.59680200
tasa_cal_mat_insuf        0.139939854    -0.01356311  0.49244527
dist_bancos               0.149847626     0.25010820  0.17740175
dist_seguridad            0.080105121     0.22236209 -0.02698953
dist_tren                 0.020139621     0.17650322  0.04826313
dist_subte                0.076064452     0.38962939 -0.06570009
dist_colectivo            0.059690329     0.06366925  0.16688595
dist_metrobus            -0.007109436     0.01083896 -0.03041651
n_dptos                  -0.024713060    -0.10478746 -0.13727293
mean_USS_M2              -0.150059683    -0.24769594 -0.38466593
std_USS_M2               -0.084863813    -0.12186251 -0.19423014
median_USS_M2            -0.150152344    -0.24391591 -0.38582410
mad_USS2_M2              -0.099876131    -0.15601691 -0.24380564
n_delitos                 0.237653130     0.28973313  0.07795078
n_delitos_hab             0.204823348     0.21804195  0.10202611
dens_pob                 -0.061712843    -0.42088751  0.05617454
robo_noviol               0.123146722     0.09785313  0.02798243
les_segvial               0.237002218     0.21871573  0.05309632
robo_viol                 0.268733676     0.27766883  0.11299983
homicidio                 1.000000000     0.13068875  0.16005167
robo_automovil            0.130688748     1.00000000 -0.02957859
p_hognbi                  0.160051668    -0.02957859  1.00000000

La función cor tiene varios argumentos * x, y: las variables (si todas son cuantitativas podemos pasar todo el dataframe) * method: qué coeficiente(s) se va(n) a usar… ¿Pearson, Spearmen o Kenndall)?

Mucho mejor es verlo en un heatmap:

select(as.data.frame(radios_delitos), -c(RADIO:HOGARES_NBI, geometry)) %>% cor(method = "pearson") %>% 
    as.data.frame() %>% mutate(Var1 = factor(row.names(.), levels = row.names(.))) %>% 
    gather(key = Var2, value = value, -Var1, na.rm = TRUE, factor_key = TRUE) %>% 
    ggplot(aes(x = Var1, y = Var2, fill = value)) + geom_tile() + scale_fill_viridis_c()

Implementemos un modelo con todas las variables

radios_delitos_l <- radios_delitos %>% select(n_delitos, everything(), -c(RADIO:HOGARES_NBI, 
    n_delitos_hab, robo_noviol:robo_automovil)) %>% st_set_geometry(NULL)
model <- lm(n_delitos ~ ., data = radios_delitos_l)

Pero momento… habíamos precisado en el primer modelo que lstat entraba en forma logarítmica…

summary(model)

Call:
lm(formula = n_delitos ~ ., data = radios_delitos_l)

Residuals:
    Min      1Q  Median      3Q     Max 
-374.73  -27.92   -8.98   11.88 1452.09 

Coefficients:
                           Estimate Std. Error t value Pr(>|t|)    
(Intercept)               5.607e+01  2.184e+01   2.567 0.010310 *  
AREA_KM2                  1.526e+02  8.852e+00  17.242  < 2e-16 ***
tasa_desoc                2.541e+00  8.219e-01   3.091 0.002010 ** 
tasa_act                  1.015e+00  2.807e-01   3.616 0.000304 ***
razon_masc               -7.755e+00  3.987e+00  -1.945 0.051836 .  
prop_mayores              3.798e+02  7.592e+01   5.003 5.93e-07 ***
tasa_cal_construct_insuf  1.061e+00  3.110e-01   3.410 0.000657 ***
tasa_cal_serv_insuf      -1.547e+00  4.223e-01  -3.663 0.000253 ***
tasa_cal_mat_insuf       -1.234e-01  2.551e-01  -0.484 0.628513    
dist_bancos              -3.631e-02  5.420e-03  -6.699 2.43e-11 ***
dist_seguridad           -9.929e-03  3.011e-03  -3.298 0.000983 ***
dist_tren                 2.231e-03  2.266e-03   0.985 0.324836    
dist_subte               -1.981e-03  1.138e-03  -1.741 0.081696 .  
dist_colectivo           -1.369e-01  1.786e-02  -7.662 2.35e-14 ***
dist_metrobus            -3.305e-03  1.588e-03  -2.081 0.037479 *  
n_dptos                   4.414e-01  3.975e-02  11.104  < 2e-16 ***
mean_USS_M2              -8.621e-03  1.327e-02  -0.650 0.515902    
std_USS_M2                2.002e-03  6.738e-03   0.297 0.766330    
median_USS_M2            -1.351e-02  1.268e-02  -1.065 0.286725    
mad_USS2_M2               1.385e-02  7.751e-03   1.787 0.074053 .  
dens_pob                 -1.137e-03  6.917e-05 -16.441  < 2e-16 ***
p_hognbi                  1.258e-01  2.057e-01   0.612 0.540901    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 66.79 on 3532 degrees of freedom
Multiple R-squared:  0.2544,    Adjusted R-squared:   0.25 
F-statistic:  57.4 on 21 and 3532 DF,  p-value: < 2.2e-16

Implementación en R lm(): multicolinealidad

Vamos a analizar la multicolinealidad entre los predictores. Usaremos la función vif() del paquete car

library(car)
Loading required package: carData

Attaching package: 㤼㸱car㤼㸲

The following object is masked from 㤼㸱package:dplyr㤼㸲:

    recode

The following object is masked from 㤼㸱package:purrr㤼㸲:

    some
vif(model)
                AREA_KM2               tasa_desoc 
                1.434081                 1.439391 
                tasa_act               razon_masc 
                1.426460                 1.258338 
            prop_mayores tasa_cal_construct_insuf 
                1.221222                 6.359161 
     tasa_cal_serv_insuf       tasa_cal_mat_insuf 
                2.537362                 2.957220 
             dist_bancos           dist_seguridad 
                1.598069                 1.266298 
               dist_tren               dist_subte 
                1.171414                 1.741578 
          dist_colectivo            dist_metrobus 
                1.399145                 1.133393 
                 n_dptos              mean_USS_M2 
                1.375091                68.771343 
              std_USS_M2            median_USS_M2 
                3.606592                58.903757 
             mad_USS2_M2                 dens_pob 
                2.550528                 1.417037 
                p_hognbi 
                3.100724 

Pareciera que las variables vinculadas a las calidades de la vivienda y la proproción de hogares con NBI se encuentran correlacionadas con más de un predictor (seguramente, muy correlacionadas entre sí). Entonces, podríamos reestimar el modelo eliminándolas.

model <- lm(n_delitos ~ . - tasa_cal_construct_insuf - tasa_cal_serv_insuf - 
    tasa_cal_mat_insuf, data = radios_delitos_l)
summary(model)

Call:
lm(formula = n_delitos ~ . - tasa_cal_construct_insuf - tasa_cal_serv_insuf - 
    tasa_cal_mat_insuf, data = radios_delitos_l)

Residuals:
    Min      1Q  Median      3Q     Max 
-393.46  -28.45   -9.31   11.56 1448.30 

Coefficients:
                 Estimate Std. Error t value Pr(>|t|)    
(Intercept)     5.558e+01  2.184e+01   2.545 0.010985 *  
AREA_KM2        1.458e+02  8.555e+00  17.040  < 2e-16 ***
tasa_desoc      2.675e+00  8.144e-01   3.284 0.001032 ** 
tasa_act        1.029e+00  2.811e-01   3.661 0.000255 ***
razon_masc     -1.059e+01  3.889e+00  -2.724 0.006477 ** 
prop_mayores    3.860e+02  7.578e+01   5.093 3.71e-07 ***
dist_bancos    -3.539e-02  5.395e-03  -6.560 6.17e-11 ***
dist_seguridad -9.158e-03  3.010e-03  -3.042 0.002366 ** 
dist_tren       1.943e-03  2.265e-03   0.857 0.391248    
dist_subte     -1.939e-03  1.138e-03  -1.703 0.088569 .  
dist_colectivo -1.216e-01  1.670e-02  -7.280 4.09e-13 ***
dist_metrobus  -3.606e-03  1.587e-03  -2.272 0.023119 *  
n_dptos         4.555e-01  3.970e-02  11.474  < 2e-16 ***
mean_USS_M2    -1.021e-02  1.329e-02  -0.769 0.442208    
std_USS_M2      2.695e-03  6.750e-03   0.399 0.689777    
median_USS_M2  -1.396e-02  1.270e-02  -1.099 0.271647    
mad_USS2_M2     1.516e-02  7.761e-03   1.954 0.050820 .  
dens_pob       -1.076e-03  6.637e-05 -16.212  < 2e-16 ***
p_hognbi        4.039e-01  1.447e-01   2.791 0.005279 ** 
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 66.93 on 3535 degrees of freedom
Multiple R-squared:  0.2506,    Adjusted R-squared:  0.2468 
F-statistic: 65.67 on 18 and 3535 DF,  p-value: < 2.2e-16

Implementación en R lm(): interacciones

Supongamos que quisiéramos agregar alguna interacción podemos hacerlo con la siguiente sintaxis p_hogaresnbi*dist_seg que agrega tanto los términos de interacción como los efectos de cada variable por separado.

Si quisiéramos introducir solamente la interacción deberíamos usar log(lstat)*age

model <- lm(n_delitos ~ AREA_KM2 * dist_seguridad, data = radios_delitos_l)
summary(model)

Call:
lm(formula = n_delitos ~ AREA_KM2 * dist_seguridad, data = radios_delitos_l)

Residuals:
    Min      1Q  Median      3Q     Max 
-658.96  -30.18  -13.53    9.50 1478.08 

Coefficients:
                          Estimate Std. Error t value Pr(>|t|)    
(Intercept)              61.985148   2.611499  23.735  < 2e-16 ***
AREA_KM2                287.861129  19.936204  14.439  < 2e-16 ***
dist_seguridad           -0.009251   0.003068  -3.016  0.00258 ** 
AREA_KM2:dist_seguridad  -0.102660   0.014590  -7.036 2.36e-12 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 72.77 on 3550 degrees of freedom
Multiple R-squared:  0.1105,    Adjusted R-squared:  0.1098 
F-statistic:   147 on 3 and 3550 DF,  p-value: < 2.2e-16
LS0tDQp0aXRsZTogIlJlZ3Jlc2lvbiBsaW5lYWwgZW4gUi4iDQphdXRob3I6ICJEci4gR2VybWFuIFJvc2F0aSINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KZmlnX3dpZHRoOiAxNQ0KZmlnX2hlaWdodDogMjAgDQotLS0NCg0KDQpgYGB7ciBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9VFJVRSwgaGlnaGxpZ2h0PVRSVUUsIGVjaG89VFJVRSkNCmBgYA0KDQojIyBJbnRyb2R1Y2Npw7NuDQpBaG9yYSBxdWUgdGVuZW1vcyBjbGFyb3MgYWxndW5vcyBhc3BlY3RvcyByZWxldmFudGVzIGRlbCBwcmVwcm9jZXNhbWllbnRvIGRlIGRhdG9zIGNvbiBgdGlkeXZlcnNlYCwgcG9kZW1vcyBlbXBlemFyIGEgcGVuc2FyIGVuIHVuIGFuw6FsaXNpcyB1biBwb2NvIG3DoXMgZWxhYm9yYWRvLiBQYXJhIGVsbG8sIHZhbW9zIGEgdXNhciBudWVzdHJvIHZpZWpvIHkgY29ub2NpZG8gZGF0YXNldCBkZSBkZWxpdG9zIHBlcm8gbG8gdmFtb3MgYSBjb21iaW5hciBjb24gaW5mb3JtYWNpw7NuIGNlbnNhbCBjb3JyZXNwb25kaWVudGUgYSBsb3MgcmFkaW9zIGRlIGxhIENBQkEuDQoNCg0KIyMjIE9iamV0aXZvcw0KKiBJbnRyb2R1Y2lyIG5vY2lvbmVzIGdlbmVyYWxlcyBkZSBtb2RlbG9zIGxpbmVhbGVzIA0KKiBQcmVzZW50YXIgbGFzIGZ1bmNpb25lcyBlbiBSIHBhcmEgZXN0aW1hciB5IGV2YWx1YXIgZGljaG9zIG1vZGVsb3MNCg0KDQojIyBQcmVwcm9jZXNhbmRvIGVsIGRhdGFzZXQNClZhbW9zIGEgY29tYmluYXIgaW5mb3JtYWNpw7NuIGRlIGxvcyByYWRpb3MgY2Vuc2FsZXMgY29uIG51ZXN0cm8gZGF0YXNldCBkZSBkZWxpdG9zLiBMYSBwcmVndW50YSBnZW5lcmFsIHF1ZSB2YW1vcyBhIGFib3JkYXIgZXMgwr9xdcOpIGNhcmFjdGVyw61zdGljYXMgdGllbmVuIGxvcyByYWRpb3MgY2Vuc2FsZXMgZW4gbG9zIHF1ZSBzZSBjb21ldGVuIGRlbGl0b3MgZW4gbGEgQ0FCQT8NCg0KYGBge3Igd2FybmluZz1GQUxTRSwgaW5jbHVkZT1GQUxTRX0NCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShsdWJyaWRhdGUpDQpsaWJyYXJ5KHNmKQ0KYGBgDQoNCg0KUHJpbWVybywgY2FyZ3VlbW9zIG51ZXN0cm8gZGF0YXNldCBkZSBkZWxpdG9zLCBmaWx0cmVtb3MgbG9zIGRhdG9zIHNpbiBpbmZvcm1hY2nDs24gZGUgbGF0aXR1ZCB5IGxvbmdpdHVkIHkgZGVtb3MgZWwgZm9ybWF0byBhZGVjdWFkbyBhIGxhcyB2YXJhaWJsZXMgZGUgZmVjaGEgeSBob3JhLg0KDQpgYGB7cn0NCmRlbGl0b3MgPC0gcmVhZC5jc3YoJy4uL2RhdGEvZGVsaXRvcy5jc3YnKQ0KDQpkZWxpdG9zIDwtIGRlbGl0b3MgJT4lIA0KICAgICAgICBmaWx0ZXIobGF0aXR1ZCE9MCB8IGxvbmdpdHVkIT0wKSAlPiUNCiAgICAgICAgbXV0YXRlKGZlY2hhPXltZChmZWNoYSksDQogICAgICAgICAgICAgICBob3JhPWhtcyhob3JhKSkNCmBgYA0KDQoNCkJpZW4sIGFob3JhIHRyYWlnYW1vcyB1biBkYXRhc2V0cyBjb24gaW5mb3JtYWNpw7NuIGFjZXJjYSBkZSBsb3MgcmFkaW9zIGNlbnNhbGVzIGRlIGxhIGNpdWRhZC4NCg0KYGBge3J9DQpyYWRpb3NfZ3JhbCA8LSBzdF9yZWFkKCcuLi9kYXRhL3JhZGlvc19pbmZvX2dyYWwuZ2VvanNvbicpDQpzdHIocmFkaW9zX2dyYWwpDQpgYGANCg0KDQpBaG9yYSBiaWVuLCBlc3RlIGRhdGFzZXQgZXMgZGUgdW4gdGlwbyBwYXJ0aWN1bGFyLiBTaSBzZSBmaWphbiBlbCDDumx0aW1vIGNhbXBvIHNlIGxsYW1hIGdlb21ldHJ5IHkgY29udGllbmUgbGEgaW5mb3JtYWNpw7NuIHBhcmEgcGxvdGVhciBsb3MgcG9saXRpY29zIGRlIGxvcyByYWRpb3MuDQoNCmBgYHtyfQ0KZ2dwbG90KCkgKyBnZW9tX3NmKGRhdGEgPSByYWRpb3NfZ3JhbCkNCmBgYA0KDQpQYXJhIHBvZGVyIG9wZXJhciBiaWVuIGNvbiBsb3MgZGF0YXNldHMgY29uanVudG9zLCB0ZW5lbW9zIHF1ZSB0cmFuc2Zvcm1hciBsb3MgZGF0b3MgZGUgZGVsaXRvcyBlbiB1biBmb3JtYXRvIGdlb2dyw6FmaWNvLiBQYXJhIGVsbG8gdXNhbW9zIGxhIGxpYnJlcsOtYSBbYHNmYF0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL3NmL3ZpZ25ldHRlcy9zZjEuaHRtbCkuDQoNCmBgYHtyfQ0KZGVsaXRvcyA8LSBzdF9hc19zZihkZWxpdG9zLCANCiAgICAgICAgICAgICAgICAgICAgY29vcmRzPWMoJ2xvbmdpdHVkJywgJ2xhdGl0dWQnKSwgDQogICAgICAgICAgICAgICAgICAgIGNycz00MzI2KQ0KYGBgDQoNCkLDoXNpY2FtZW50ZSwgbGUgcGFzYW1vcyBhIGxhIGZ1bmNpw7NuIGBzdF9hc19zZmAgbGFzIGNvbHVtbmFzIHF1ZSBjb250aWVuZW4gbGEgaW5mb3JtYWNpw7NuIGdlb2dyw6FmaWNhIGRlIGNvb3JkZW5hZGFzIChgY29vcmRzYCkgeSBsYSBwcm95ZWNjacOzbiAoYGNyc2ApIGVuIGxhIHF1ZSBlc3TDoW4gbG9zIGRhdG9zLg0KDQpBaG9yYSwgdmFtb3MgYSBoYWNlciBsYXMgY29zYXMgaW50ZXJlc2FudGVzLiBMYSBpZGVhIGVzIHBvZGVyIHJlYWxpemFyIHVuYSBvcGVyYWNpw7NuIHF1ZSBzZSBsbGFtYSAiam9pbiBlc3BhY2lhbCIuIFZhbW9zIGEgdHJhdGFyIGRlIGNvbnRhciBjdcOhbnRvcyBkZWxpdG9zIGh1Ym8gZW4gY2FkYSB1bm8gZGUgbG9zIHJhZGlvcyBjZW5zYWxlcy4NCg0KYGBge3J9DQpnZ3Bsb3QoKSArIA0KICAgICAgICBnZW9tX3NmKGRhdGE9cmFkaW9zX2dyYWwpICsgDQogICAgICAgIGdlb21fc2YoZGF0YT1zYW1wbGVfbihkZWxpdG9zLDEwMDApLCBhZXMoY29sb3I9dGlwb19kZWxpdG8pKSArIA0KICAgICAgICBmYWNldF93cmFwKH50aXBvX2RlbGl0bykNCmBgYA0KDQoNCkxhIG9wZXJhY2nDs24gdmEgYSBjb25zdGFyIGRlIGRvcyBwYXJ0ZXM6IGxhIHByaW1lcmEsIHZlbW9zIGEgam9pbmVhciBsb3MgZGF0b3MgZGUgZGVsaXRvcyBjb24gbG9zIGxvcyBkZSByYWRpby4gR2VuZXJhbW9zIHVuYSBudWV2byBvYmpldG8gbGxhbWFkbyBgZGVsaXRvc19yYWRpb3NgIHF1ZSBjb250aWVuZSBwYXJhIGNhZGEgZGVsaXRvLCBsYSBpbmZvcm1hY2nDs24gZGVsIHRhZGlvIGFsIHF1ZSBwZXJ0ZW5lY2UuDQoNClVzYW1vcyBsYSBmdW5jacOzbiBgc3Rfam9pbmAgcXVlIHNpcnZlIHBhciByZWFsaXphciBlc3RhcyBvcGVyYWNpb25lcy4gUGFzYW1vcyBhbWJvcyBvYmpldG9zIGNvbW8gYXJndW1lbnRvcyB5IHVzYW1vcyBlbCBhcmd1bWVudG8gYHN0X3dpdGhpbmAuDQogICAgICAgIA0KYGBge3J9DQpkZWxpdG9zX3JhZGlvIDwtIHN0X2pvaW4oZGVsaXRvcywgDQogICAgICAgICAgICAgICAgICAgICAgICAgIHJhZGlvc19ncmFsLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgam9pbiA9IHN0X3dpdGhpbikgJT4lDQogICAgICAgIGFzX3RpYmJsZSgpIA0KaGVhZChkZWxpdG9zKQ0KYGBgDQoNCkx1ZWdvLCBjb250YW1vcyBsYSBjYW50aWRhZCBkZSBwdW50b3MgZW4gY2FkYSByYWRpbyBjb24gdW4gc2ltcGxlIGBncm91cF9ieSArIHN1bW1hcmlzZTBgIHkgcmVhbGl6YW1vcyB1biBgbGVmdF9qb2luYCBjb24gbGEgdGFibGEgb3JpZ2luYWwgZGUgcmFkaW9zLg0KDQpgYGB7cn0NCnJhZGlvc19kZWxpdG9zIDwtIGRlbGl0b3NfcmFkaW8gJT4lDQogICAgICAgIGdyb3VwX2J5KFJBRElPX0lEKSAlPiUNCiAgICAgICAgc3VtbWFyaXNlKG5fZGVsaXRvcz1uKCkpICU+JQ0KICAgICAgICBsZWZ0X2pvaW4oeD1yYWRpb3NfZ3JhbCkNCmBgYA0KDQoNClZlbW9zLCBhaG9yYSwgcXVlIGVsIG9iamV0byBgcmFkaW9zX2RlbGl0b3NgIGVzIGlndWFsIGEgYHJhZGlvc19nZW5lcmFsYCBwZXJvIGNvbiB1bmEgY29sdW1uYSBtw6FzIHF1ZSBzZSBsbGFtYSBgbl9kZWxpdG9zYA0KDQpgYGB7cn0NCnN0cihyYWRpb3NfZGVsaXRvcykNCmBgYA0KDQoNCkhhZ2Ftb3MgdW4gcHJpbWVyIG1hcGEgYSB2ZXIgcXXDqSBwYXNhLi4uDQoNCmBgYHtyfQ0KZ2dwbG90KCkgKw0KICAgICAgICBnZW9tX3NmKGRhdGE9cmFkaW9zX2RlbGl0b3MsIA0KICAgICAgICAgICAgICAgIGNvbG9yPU5BLA0KICAgICAgICAgICAgICAgIGFlcyhmaWxsPW5fZGVsaXRvcykpICsgDQogICAgICAgIHNjYWxlX2ZpbGxfdmlyaWRpc19jKCkgKyANCiAgICAgICAgdGhlbWVfbWluaW1hbCgpDQoNCmBgYA0KDQoNCkFob3JhIGJpZW4sIHBhcmVjaWVyYSBxdWUgbG9zIHJhZGlvcyBjb24gbWF5b3IgY2FudGlkYWQgZGUgZGVsaXRvcyBzb24gbG9zIHF1ZSBzZSBlbmN1ZW50cmFuIGVuIGVsIGNlbnRybyBkZSBsYSBjaXVkYWQgbyBlbiB6b25hcyBkZSBhbHRhIHBvYmxhY2nDs24uIFBvciBlbGxvLCBjcmVlbW9zIHVuYSBtZWRpZGEgZGUgImRlbGl0b3MgLyBoYWJpdGFudGVzIiBwYXJhIGNvbnRyb2xhciBlc3RlIGZhY3RvciB5IGFwcm92ZWNoZW1vcyBwYXJhIGNhbGN1bGFyIHVuYSB2YXJpYWJsZSBkZSBkZW5zaWRhZCBwb2JsYWNpb25hbDoNCg0KYGBge3J9DQpyYWRpb3NfZGVsaXRvcyA8LSByYWRpb3NfZGVsaXRvcyAlPiUNCiAgICAgICAgbXV0YXRlKG5fZGVsaXRvc19oYWI9IG5fZGVsaXRvcyAvIFBPQkxBQ0lPTiwNCiAgICAgICAgICAgICAgIGRlbnNfcG9iPVBPQkxBQ0lPTi9BUkVBX0tNMikNCmBgYA0KDQoNClkgdmVhbW9zIGFob3JhIGVsIHBsb3QuLi4NCg0KDQpgYGB7cn0NCmdncGxvdCgpICsNCiAgICAgICAgZ2VvbV9zZihkYXRhPXJhZGlvc19kZWxpdG9zLCANCiAgICAgICAgICAgICAgICBjb2xvcj1OQSwNCiAgICAgICAgICAgICAgICBhZXMoZmlsbD1uX2RlbGl0b3NfaGFiKSkgKyANCiAgICAgICAgc2NhbGVfZmlsbF92aXJpZGlzX2MoKSArIA0KICAgICAgICB0aGVtZV9taW5pbWFsKCkNCg0KYGBgDQoNCkFob3JhIGxhIGNvc2EgcGFyZWNlIGJhc3RhbnRlIG3DoXMgaG9tZ8OpbmVhLi4uIHBlcm8gcG9ycXVlIHRlbmVtb3MgdW4gY2FzbyBtdXkgcmFyby4gVmFtb3MgYSB0cnVuY2FyIGxvcyB2YWxvcmVzIGEgdW4gbcOheGltbw0KDQoNCmBgYHtyfQ0KcmFkaW9zX2RlbGl0b3MgPC0gcmFkaW9zX2RlbGl0b3MgJT4lDQogICAgICAgIG11dGF0ZShuX2RlbGl0b3NfaGFiID0gaWZlbHNlKG5fZGVsaXRvc19oYWIgPj0wLjYsIDAuNiwgbl9kZWxpdG9zX2hhYikpDQoNCmBgYA0KDQoNCmBgYHtyfQ0KcmFkaW9zX2RlbGl0b3MgJT4lDQpnZ3Bsb3QoKSArDQogICAgICAgIGdlb21fc2YoIA0KICAgICAgICAgICAgICAgIGNvbG9yPU5BLA0KICAgICAgICAgICAgICAgIGFlcyhmaWxsPW5fZGVsaXRvc19oYWIpKSArIA0KICAgICAgICBzY2FsZV9maWxsX3ZpcmlkaXNfYygpICsgDQogICAgICAgIHRoZW1lX21pbmltYWwoKQ0KYGBgDQoNCg0KIyMjIENvbnNpZ25hDQoqIEFncmVnYXIgYWwgb2JqZXRvIGByYWRpb3NfZGVsaXRvc2AgY29sdW1uYXMgcXVlIGN1ZW50ZW4gcGFyYSBjYWRhIHJhZGlvIGNhZGEgdW5vIGRlIGxvcyB0aXBvcyBkZSBkZWxpdG9zIGVuIGVsIG9iamV0byBgZGVsaXRvLiBOb21icmFyIGxhcyB2YXJpYWJsZXMgcmVzdWx0YW50ZXMgZGUgbGEgc2lndWllbnRlIGZvcm1hOg0KDQoqIGhvbV9kb2xvc28NCiogaG9tX3NlZ3ZpYWwNCiogaHVydG9fc3YNCiogaHVydG9fYXV0bw0KKiBsZXNfc2VndmlhbA0KKiByb2JvX3YNCiogcm9ib19hdXRvDQoNCg0KYGBge3J9DQpyYWRpb3NfZGVsaXRvcyA8LSBkZWxpdG9zX3JhZGlvICU+JQ0KICAgICAgICBncm91cF9ieShSQURJT19JRCwgdGlwb19kZWxpdG8pICU+JQ0KICAgICAgICBzdW1tYXJpemUobj1uKCkpICU+JQ0KICAgICAgICBzcHJlYWQoa2V5ID0gdGlwb19kZWxpdG8sIHZhbHVlID0gbikgJT4lDQogICAgICAgIGxlZnRfam9pbih4PXJhZGlvc19kZWxpdG9zKSAlPiUNCiAgICAgICAgcmVwbGFjZSguLCBpcy5uYSguKSwwKSAlPiUNCiAgICAgICAgcmVuYW1lKGhvbV9kb2xvc289J0hvbWljaWRpbyBEb2xvc28nLA0KICAgICAgICAgICAgICAgaG9tX3NlZ3ZpYWw9J0hvbWljaWRpbyBTZWcgVmlhbCcsDQogICAgICAgICAgICAgICByb2JvX25vdmlvbD0nSHVydG8gKFNpbiB2aW9sZW5jaWEpJywNCiAgICAgICAgICAgICAgIGh1cnRvX2F1dG8gPSAnSHVydG8gQXV0b21vdG9yJywNCiAgICAgICAgICAgICAgIGxlc19zZWd2aWFsPSdMZXNpb25lcyBTZWcgVmlhbCcsDQogICAgICAgICAgICAgICByb2JvX3Zpb2w9J1JvYm8gKENvbiB2aW9sZW5jaWEpJywNCiAgICAgICAgICAgICAgIHJvYm9fYXV0bz0nUm9ibyBBdXRvbW90b3InDQogICAgICAgICAgICAgICApDQoNCmBgYA0KDQpZIG9yZGVuYW1vcyB1biBwb2NvIGVsIGRhdGFzZXQuLi4NCg0KYGBge3J9DQoNCnJhZGlvc19kZWxpdG9zPC0gcmFkaW9zX2RlbGl0b3MgJT4lDQogICAgICAgIG11dGF0ZShob21pY2lkaW8gPSBob21fZG9sb3NvICsgaG9tX3NlZ3ZpYWwsDQogICAgICAgICAgICAgICByb2JvX2F1dG9tb3ZpbCA9IGh1cnRvX2F1dG8gKyByb2JvX2F1dG8pICU+JQ0KICAgICAgICBzZWxlY3QoLWhvbV9kb2xvc28sIC1ob21fc2VndmlhbCwgLWh1cnRvX2F1dG8sIC1yb2JvX2F1dG8pDQogICAgICAgIA0KYGBgDQoNCg0KKiBHZW5lcmVtb3MgdW5hIHZhcmlhYmxlIGNvbiBsYSBwcm9wb3JjacOzbiBkZSBIb2dhcmVzIE5CSQ0KDQoNCmBgYHtyLCBoaWdobGlnaHQ9VFJVRSwgcHJvbXB0PVRSVUUsIHN0cmlwLndoaXRlPUZBTFNFLCBjb2xsYXBzZT1UUlVFLCB0aWR5PVRSVUV9DQoNCnJhZGlvc19kZWxpdG9zIDwtIHJhZGlvc19kZWxpdG9zICU+JQ0KICAgICAgICBtdXRhdGUocF9ob2duYmk9IEhPR0FSRVNfTkJJIC8gSE9HQVJFUyAqIDEwMCkgJT4lDQogICAgICAgIHJlcGxhY2VfbmEobGlzdChwX2hvZ25iaT0wKSkNCmBgYA0KDQoNCiMjIEludHJvZHVjY2nDs24gYSBsYSByZWdyZXNpw7NuIGxpbmVhbCBlbiBSDQpUb2RvcyBub3MgYWNvcmRhbW9zIGRlbCBtb2RlbG8gbGluZWFsOg0KDQokJFlfe2l9PVxiZXRhX3swfSArIFxiZXRhX3sxfSpYX3tpfSArIFxlcHNpbG9uX3tpfSQkDQoNCiogTG9zIHBhcsOhbWV0cm9zIGRlbCBtb2RlbG8gc29uIG11eSBmw6FjaWxlcyBkZSBpbnRlcnByZXRhcjoNCg0KJFxiZXRhX3swfSQgZXMgZWwgaW50ZXJjZXB0bw0KDQokXGJldGFfezF9JCBlcyBsYSBwZW5kaWVudGUgZGUgbGEgdmFyaWFibGUgJFgkOyBlcyBkZWNpciBlbCBlZmVjdG8gbWVkaW8gZW4gJFkkIGN1YW5kbyAkWCQgc2UgaW5jcmVtZW50YSBlbiB1bmEgdW5pZGFkICh5IHRvZG8gbG8gZGVtw6FzLCBzZSBtYW50aWVuZSBjb25zdGFudGUpDQoNCiRcZXBzaWxvbl97aX0kIGVzIGVsIGVycm9yIG8gcmVzaWR1byBkZSBlc3RpbWFjacOzbg0KDQpFbiB1biBtb2RlbG8gbGluZWFsIGJ1c2NhbW9zICJtaW5pbWl6YXIiIHVuYSBkZXRlcm1pbmFkYSBtw6l0cmljYSBkZSBlcnJvci4gRW4gcGFydGljdWxhciwgYnVzY2Ftb3MgaGFjZXIgbcOtbmltbyBlbCBlcnJvciBjdWFkcsOhdGljbyBtZWRpbyAoTVNFKToNCg0KJCRNU0U9XGZyYWN7MX17bn1cc3VtX3tpPTF9XntufShZX3tpfS1caGF0e1lfe2l9fSleMiQkDQoNCg0KIyMgSW1wbGVtZW50YWNpw7NuIGVuIFIgYGBsbSgpYGANClBhcmEgaW1wbGVtZW50YXIgZW4gUiB1bmEgcmVncmVzacOzbiBsaW5lYWwgc2ltcGxlIHVzYW1vcyBsYSBmdW5jacOzbiBgYGxtKClgYA0KKiBgYGZvcm11bGFgYDogdW5hIGV4cHJlc2nDs24gY29uIGxhIHNpZ3VpZW50ZSBmb3JtYTogYGB5fnhgYA0KKiBgYGRhdGFgYDogZGF0YWZyYW1lIG8gZGF0YW1hdHJpeCBhIHV0aWxpemFyDQoqIGBgc3Vic2V0YGA6IHVuIHZlY3RvciBxdWUgZGVmaW5lIHVuIHN1YmNvbmp1bnRvIGRlIGRhdG9zIGEgdXNhciBlbiBlbCBtb2RlbG8NCiogYGB3ZWlnaHRzYGA6IHZlY3RvciBxdWUgZGVmaW5lIHBlc29zIHBhcmEgbGEgcmVncmVzacOzbiAoV0xOUykNCg0KDQpgYGB7ciwgaGlnaGxpZ2h0PVRSVUUsIHByb21wdD1UUlVFLCBzdHJpcC53aGl0ZT1GQUxTRSwgY29sbGFwc2U9VFJVRSwgdGlkeT1UUlVFfQ0KDQptb2RlbCA8LSBsbShmb3JtdWxhID0gbl9kZWxpdG9zfiBBUkVBX0tNMiwgZGF0YT1yYWRpb3NfZGVsaXRvcykNCg0KYGBgDQoNClNpIGltcHJpbWltb3MgZWwgbW9kZWxvLi4uIHNvbGFtZW50ZSBub3MgZGEgdW5hIGluZm9ybWFjacOzbiBiw6FzaWNhOiBlbCBpbnRlcmNlcHRvIHkgZWwgdmFsb3IgZGUgbGEgcGVuZGllbnRlLg0KDQpgYGB7ciwgaGlnaGxpZ2h0PVRSVUUsIHByb21wdD1UUlVFLCBzdHJpcC53aGl0ZT1GQUxTRSwgY29sbGFwc2U9VFJVRSwgdGlkeT1UUlVFfQ0Kc3VtbWFyeShtb2RlbCkNCmBgYA0KQWhvcmEgdGVuZW1vcyBhY2Nlc28gYSBtdWNoYSBtw6FzIGluZm9ybWFjacOzbjoNCiogcC12YWxvcmVzIHkgZXJyb3JlcyBlc3TDoW5kYXIgZGUgbG9zIGNvZWZpY2llbnRlcy4gwr9Tb24gc2lnbmlmaWNhdGl2b3M/DQoqICRSXjIkOiBtZW5vcyBkZWwgMTAlIGRlIGxhIHZhcmlhbmNpYSBkZSBsYSB2YXJpYWJsZSBkZXBlbmRpZW50ZSBlcyBleHBsaWNhZGEgcG9yIGVsIG1vZGVsbw0KDQpWZWFtb3MgcXXDqSBoYXkgZGVudHJvIGRlbCBvYmpldG8gYG1vZGVsYC4gVXNhbW9zIGxhIGZ1bmNpw7NuIGBgbmFtZXNgYCBwYXJhIGFjY2VkZXIgYSBsb3Mgb2JqZXRvcyBkZW50cm8gZGVsIG9iamV0byBgYG1vZGVsYGAuIEx1ZWdvLCBwb2RlbW9zIGlyIHVzYW5kbyBsb3Mgbm9tYnJlcyBwYXJhIGFjY2VkZXIgYSBsb3MgZGlmZXJlbnRlcyBlbGVtZW50b3MuDQoNCiAgICAgDQpgYGB7ciwgaGlnaGxpZ2h0PVRSVUUsIHByb21wdD1UUlVFLCBzdHJpcC53aGl0ZT1GQUxTRSwgY29sbGFwc2U9VFJVRSwgdGlkeT1UUlVFfQ0KbmFtZXMobW9kZWwpDQpgYGANCg0KDQpgYGB7ciwgaGlnaGxpZ2h0PVRSVUUsIHByb21wdD1UUlVFLCBzdHJpcC53aGl0ZT1GQUxTRSwgY29sbGFwc2U9VFJVRSwgdGlkeT1UUlVFfQ0KbW9kZWwkY29lZmZpY2llbnRzDQpgYGANCg0KIyMgSW1wbGVtZW50YWNpw7NuIGVuIFIgYGBsbSgpYGA6IEFsZ3VuYXMgZnVuY2lvbmVzIMO6dGlsZXM6DQoNCk9idGVuZW1vcyBpbnRlcnZhbG9zIGRlIGNvbmZpYW56YSBkZSBsb3MgcGFyw6FtZXRyb3MgZGVsIG1vZGVsby4NCg0KYGBge3IsIGhpZ2hsaWdodD1UUlVFLCBwcm9tcHQ9VFJVRSwgc3RyaXAud2hpdGU9RkFMU0UsIGNvbGxhcHNlPVRSVUUsIHRpZHk9VFJVRX0NCmNvZWYobW9kZWwpDQpjb25maW50KG1vZGVsLGxldmVsPTAuOTUpDQpgYGANCg0KVmVhbW9zIGxhIGZ1bmNpw7NuIGBgcHJlZGljdCgpYGANCg0KYGBge3IsIGhpZ2hsaWdodD1UUlVFLCBwcm9tcHQ9VFJVRSwgc3RyaXAud2hpdGU9RkFMU0UsIGNvbGxhcHNlPVRSVUUsIHRpZHk9VFJVRX0NCnByZWRpY3QobW9kZWwsIGRhdGEuZnJhbWUoQVJFQV9LTTIgPSBjKDEsIDEwLCAxMDApLCBpbnRlcnZhbCA9ICJwcmVkaWN0aW9uIikpDQpgYGANCg0KRXMgZGVjaXIsIHF1ZSBwYXJhIHNpIGxhIHZhcmlhYmxlIGluZGVwZW5kaWVudGUgYEFSRUFfS00yYCBwcmVzZW50YXJhIGxvcyB2YWxvcmVzIDEsIDEwIHkgMTAwLCBsYSB2YXJpYWJsZSBkZXBlbmRpZW50ZSBgbl9kZWxpdG9zYCBwcmVzZW50YXLDrWEgZXNvcyB2YWxvcmVzIChlbiBsYSBtZWRpYSkuDQoNCkdyYWZpcXVlbW9zLCBhaG9yYSwgdG9kby4NCg0KYGBge3IsIGhpZ2hsaWdodD1UUlVFLCBwcm9tcHQ9VFJVRSwgc3RyaXAud2hpdGU9RkFMU0UsIGNvbGxhcHNlPVRSVUUsIHRpZHk9VFJVRX0NCmdncGxvdChkYXRhPXJhZGlvc19kZWxpdG9zLCBhZXMoeD1BUkVBX0tNMiwgeT1uX2RlbGl0b3MpKSArIA0KICAgICAgICBnZW9tX3BvaW50KCkgKyANCiAgICAgICAgZ2VvbV9zbW9vdGgobWV0aG9kPSdsbScpDQpgYGANCg0KR2VuZXJlbW9zIHVuIG1vZGVsbywgYWhvcmEsIHF1ZSBjb250ZW5nYSB0b2RhcyBsYXMgdmFyaWFibGVzIGRlbCBkYXRhc2V0LiBWZWFtb3MsIHByaW1lcm8sIGxhIGNvcnJlbGFjacOzbiBlbnRyZSB2YXJpYXMgdmFyaWFibGVzOiANCg0KYGBge3IsIGhpZ2hsaWdodD1UUlVFLCBwcm9tcHQ9VFJVRSwgc3RyaXAud2hpdGU9RkFMU0UsIGNvbGxhcHNlPVRSVUUsIHRpZHk9VFJVRX0NCnNlbGVjdChhcy5kYXRhLmZyYW1lKHJhZGlvc19kZWxpdG9zKSwgLWMoUkFESU86SE9HQVJFU19OQkksZ2VvbWV0cnkpKSAlPiUNCiAgICAgICAgY29yKG1ldGhvZCA9ICdwZWFyc29uJykNCmBgYA0KDQpMYSBmdW5jacOzbiBgYGNvcmBgIHRpZW5lIHZhcmlvcyBhcmd1bWVudG9zDQoqIGBgeGBgLCBgYHlgYDogbGFzIHZhcmlhYmxlcyAoc2kgdG9kYXMgc29uIGN1YW50aXRhdGl2YXMgcG9kZW1vcyBwYXNhciB0b2RvIGVsIGRhdGFmcmFtZSkNCiogYGBtZXRob2RgYDogcXXDqSBjb2VmaWNpZW50ZShzKSBzZSB2YShuKSBhIHVzYXIuLi4gwr9QZWFyc29uLCBTcGVhcm1lbiBvIEtlbm5kYWxsKT8NCg0KTXVjaG8gbWVqb3IgZXMgdmVybG8gZW4gdW4gaGVhdG1hcDoNCg0KDQpgYGB7ciwgaGlnaGxpZ2h0PVRSVUUsIHByb21wdD1UUlVFLCBzdHJpcC53aGl0ZT1GQUxTRSwgY29sbGFwc2U9VFJVRSwgdGlkeT1UUlVFfQ0Kc2VsZWN0KGFzLmRhdGEuZnJhbWUocmFkaW9zX2RlbGl0b3MpLCAtYyhSQURJTzpIT0dBUkVTX05CSSxnZW9tZXRyeSkpICU+JQ0KICAgICAgICBjb3IobWV0aG9kID0gJ3BlYXJzb24nKSAlPiUNCiAgICAgICAgYXMuZGF0YS5mcmFtZSgpICU+JQ0KICAgICAgICBtdXRhdGUoVmFyMSA9IGZhY3Rvcihyb3cubmFtZXMoLiksIGxldmVscz1yb3cubmFtZXMoLikpKSAlPiUgDQogICAgICAgIGdhdGhlcihrZXkgPSBWYXIyLCB2YWx1ZSA9IHZhbHVlLCAtVmFyMSwgbmEucm0gPSBUUlVFLCBmYWN0b3Jfa2V5ID0gVFJVRSkgICU+JQ0KICAgICAgICBnZ3Bsb3QoYWVzKHg9VmFyMSwgeT1WYXIyLCBmaWxsPXZhbHVlKSkgKw0KICAgICAgICAgICAgICAgICAgICAgICAgZ2VvbV90aWxlKCkgKyANCiAgICAgICAgICAgICAgICAgICAgICAgIHNjYWxlX2ZpbGxfdmlyaWRpc19jKCkNCmBgYA0KDQoNCkltcGxlbWVudGVtb3MgdW4gbW9kZWxvIGNvbiB0b2RhcyBsYXMgdmFyaWFibGVzDQoNCmBgYHtyLCBoaWdobGlnaHQ9VFJVRSwgcHJvbXB0PVRSVUUsIHN0cmlwLndoaXRlPUZBTFNFLCBjb2xsYXBzZT1UUlVFLCB0aWR5PVRSVUV9DQoNCnJhZGlvc19kZWxpdG9zX2wgPC0gcmFkaW9zX2RlbGl0b3MgJT4lDQogICAgICAgIHNlbGVjdChuX2RlbGl0b3MsIGV2ZXJ5dGhpbmcoKSwgLWMoUkFESU86SE9HQVJFU19OQkksIG5fZGVsaXRvc19oYWIsIHJvYm9fbm92aW9sOnJvYm9fYXV0b21vdmlsKSkgJT4lDQogICAgICAgIHN0X3NldF9nZW9tZXRyeShOVUxMKQ0KDQptb2RlbCA8LSBsbShuX2RlbGl0b3N+LiwgZGF0YT1yYWRpb3NfZGVsaXRvc19sKQ0KYGBgDQoNCg0KUGVybyBtb21lbnRvLi4uIGhhYsOtYW1vcyBwcmVjaXNhZG8gZW4gZWwgcHJpbWVyIG1vZGVsbyBxdWUgYGBsc3RhdGBgIGVudHJhYmEgZW4gZm9ybWEgbG9nYXLDrXRtaWNhLi4uDQoNCmBgYHtyLCBoaWdobGlnaHQ9VFJVRSwgcHJvbXB0PVRSVUUsIHN0cmlwLndoaXRlPUZBTFNFLCBjb2xsYXBzZT1UUlVFLCB0aWR5PVRSVUV9DQpzdW1tYXJ5KG1vZGVsKQ0KYGBgDQoNCg0KIyMgSW1wbGVtZW50YWNpw7NuIGVuIFIgYGBsbSgpYGA6IG11bHRpY29saW5lYWxpZGFkDQpWYW1vcyBhIGFuYWxpemFyIGxhIG11bHRpY29saW5lYWxpZGFkIGVudHJlIGxvcyBwcmVkaWN0b3Jlcy4gVXNhcmVtb3MgbGEgZnVuY2nDs24gYGB2aWYoKWBgIGRlbCBwYXF1ZXRlIGBgY2FyYGANCg0KYGBge3IsIGhpZ2hsaWdodD1UUlVFLCBwcm9tcHQ9VFJVRSwgc3RyaXAud2hpdGU9RkFMU0UsIGNvbGxhcHNlPVRSVUUsIHRpZHk9VFJVRX0NCg0KbGlicmFyeShjYXIpDQp2aWYobW9kZWwpDQpgYGANCg0KUGFyZWNpZXJhIHF1ZSBsYXMgdmFyaWFibGVzIHZpbmN1bGFkYXMgYSBsYXMgY2FsaWRhZGVzIGRlIGxhIHZpdmllbmRhIHkgbGEgcHJvcHJvY2nDs24gZGUgaG9nYXJlcyBjb24gTkJJIHNlIGVuY3VlbnRyYW4gY29ycmVsYWNpb25hZGFzIGNvbiBtw6FzIGRlIHVuIHByZWRpY3RvciAoc2VndXJhbWVudGUsIG11eSBjb3JyZWxhY2lvbmFkYXMgZW50cmUgc8OtKS4gRW50b25jZXMsIHBvZHLDrWFtb3MgcmVlc3RpbWFyIGVsIG1vZGVsbyBlbGltaW7DoW5kb2xhcy4NCg0KYGBge3IsIGhpZ2hsaWdodD1UUlVFLCBwcm9tcHQ9VFJVRSwgc3RyaXAud2hpdGU9RkFMU0UsIGNvbGxhcHNlPVRSVUUsIHRpZHk9VFJVRX0NCm1vZGVsIDwtIGxtKG5fZGVsaXRvc34uIC10YXNhX2NhbF9jb25zdHJ1Y3RfaW5zdWYgLXRhc2FfY2FsX3NlcnZfaW5zdWYgLXRhc2FfY2FsX21hdF9pbnN1ZiwgZGF0YSA9IHJhZGlvc19kZWxpdG9zX2wpDQoNCnN1bW1hcnkobW9kZWwpDQpgYGANCg0KIyMgSW1wbGVtZW50YWNpw7NuIGVuIFIgYGBsbSgpYGA6IGludGVyYWNjaW9uZXMgDQoNClN1cG9uZ2Ftb3MgcXVlIHF1aXNpw6lyYW1vcyBhZ3JlZ2FyIGFsZ3VuYSBpbnRlcmFjY2nDs24gcG9kZW1vcyBoYWNlcmxvIGNvbiBsYSBzaWd1aWVudGUgc2ludGF4aXMgYGBwX2hvZ2FyZXNuYmkqZGlzdF9zZWdgYCBxdWUgYWdyZWdhIHRhbnRvIGxvcyB0w6lybWlub3MgZGUgaW50ZXJhY2Npw7NuIGNvbW8gbG9zIGVmZWN0b3MgZGUgY2FkYSB2YXJpYWJsZSBwb3Igc2VwYXJhZG8uIA0KDQpTaSBxdWlzacOpcmFtb3MgaW50cm9kdWNpciBzb2xhbWVudGUgbGEgaW50ZXJhY2Npw7NuIGRlYmVyw61hbW9zIHVzYXIgYGBsb2cobHN0YXQpKmFnZWBgDQpgYGB7ciwgaGlnaGxpZ2h0PVRSVUUsIHByb21wdD1UUlVFLCBzdHJpcC53aGl0ZT1GQUxTRSwgY29sbGFwc2U9VFJVRSwgdGlkeT1UUlVFfQ0KbW9kZWw8LWxtKG5fZGVsaXRvcyB+IEFSRUFfS00yKmRpc3Rfc2VndXJpZGFkLCBkYXRhPXJhZGlvc19kZWxpdG9zX2wpDQpzdW1tYXJ5KG1vZGVsKQ0KYGBgDQoNCg==