Planeación estratégica basada en analítica prescriptiva

Grupo 503

Profesor Rodolfo Miguel Gameros

Equipo 7:

A00833113 - Avril Lobato

A01771127 - Lesly Darian Romero Vázquez

A00831105 - Jazmín del Carmen Cortez Mendoza

A01284611 - Lisset Hernández

Análisis Exploratorio de Datos Espaciales (ESDA: Exploratory Spatial Data Analysis)

El Análisis exploratorio de datos espacial involucra diferentes herramientas para visualizar los patrones espaciales existentes en la base de datos. Estos datos se pueden segregar o descomponer los patrones espaciales en diferentes elementos a lo largo des mapa, y a su vez medir la extensión de estas autocorrelaciones espaciales. El principal paquete de R que se utiliza para este propósito es ‘geostan’.

Importación de bases de datos y geocercas

A nivel estatal

mx_state <- read.csv("state_data.csv")
head(mx_state)
##                 state year state_id crime_rate unemployment employment
## 1      Aguascalientes 2021     1057       6.75         0.04       0.97
## 2     Baja California 2021     2304      84.67         0.01       0.98
## 3 Baja California Sur 2021     2327       8.52         0.03       0.97
## 4            Campeche 2021     1086       9.22         0.02       0.98
## 5             Chiapas 2021     1182      10.01         0.05       0.97
## 6           Chihuahua 2021      888      71.98         0.04       0.97
##   business_activity real_wage real_ave_month_income pop_density lq_primary
## 1             -1.90    361.02               5641.67      261.21       0.16
## 2              2.47    388.22               7599.62       53.19       0.47
## 3             -2.12    345.57               8660.90       11.27       0.73
## 4             -2.44    414.48               5357.29       16.42       0.73
## 5             -2.41    312.37               6581.28       77.16       1.56
## 6             -1.25    362.93               5708.75       15.30       0.64
##   lq_secondary lq_tertiary new_fdi_real_mxn log_new_fdi_real_mxn  region_a
## 1         1.24        1.00          1270.03                 3.10     Bajio
## 2         1.62        0.86         20407.81                 4.31     Norte
## 3         0.51        1.13         19022.55                 4.28 Occidente
## 4         0.78        1.06          2390.64                 3.38       Sur
## 5         0.88        0.99           189.58                 2.28       Sur
## 6         1.97        0.80          7153.63                 3.86     Norte
##   region_b
## 1        2
## 2        3
## 3        4
## 4        5
## 5        5
## 6        3
glimpse(mx_state)
## Rows: 32
## Columns: 17
## $ state                 <chr> "Aguascalientes", "Baja California", "Baja Calif…
## $ year                  <int> 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, …
## $ state_id              <int> 1057, 2304, 2327, 1086, 1182, 888, 1114, 933, 11…
## $ crime_rate            <dbl> 6.75, 84.67, 8.52, 9.22, 10.01, 71.98, 11.58, 4.…
## $ unemployment          <dbl> 0.04, 0.01, 0.03, 0.02, 0.05, 0.04, 0.06, 0.04, …
## $ employment            <dbl> 0.97, 0.98, 0.97, 0.98, 0.97, 0.97, 0.94, 0.94, …
## $ business_activity     <dbl> -1.90, 2.47, -2.12, -2.44, -2.41, -1.25, -2.08, …
## $ real_wage             <dbl> 361.02, 388.22, 345.57, 414.48, 312.37, 362.93, …
## $ real_ave_month_income <dbl> 5641.67, 7599.62, 8660.90, 5357.29, 6581.28, 570…
## $ pop_density           <dbl> 261.21, 53.19, 11.27, 16.42, 77.16, 15.30, 6195.…
## $ lq_primary            <dbl> 0.16, 0.47, 0.73, 0.73, 1.56, 0.64, 0.03, 0.17, …
## $ lq_secondary          <dbl> 1.24, 1.62, 0.51, 0.78, 0.88, 1.97, 0.58, 1.55, …
## $ lq_tertiary           <dbl> 1.00, 0.86, 1.13, 1.06, 0.99, 0.80, 1.14, 0.93, …
## $ new_fdi_real_mxn      <dbl> 1270.03, 20407.81, 19022.55, 2390.64, 189.58, 71…
## $ log_new_fdi_real_mxn  <dbl> 3.10, 4.31, 4.28, 3.38, 2.28, 3.86, 4.54, 3.80, …
## $ region_a              <chr> "Bajio", "Norte", "Occidente", "Sur", "Sur", "No…
## $ region_b              <int> 2, 3, 4, 5, 5, 3, 1, 3, 4, 4, 2, 5, 1, 4, 1, 4, …

Seguidamente, se leen los archivos de las formas geométricas (geocercas) que determinarán los mapas a realizar después para visualizar los datos.

A nivel estado (32)

mx_state_map <- st_read("mexlatlong.shp")
## Reading layer `mexlatlong' from data source 
##   `C:\Users\AVRIL\Downloads\mexlatlong.shp' using driver `ESRI Shapefile'
## Simple feature collection with 32 features and 19 fields
## Geometry type: MULTIPOLYGON
## Dimension:     XY
## Bounding box:  xmin: -118.4042 ymin: 14.55055 xmax: -86.73862 ymax: 32.71846
## CRS:           NA
#mx_state_map <- read_sf("mexlatlong.shp")

Basic Map Making

### Basic Map Making
state_geodata <- geo_join(mx_state_map,mx_state,'OBJECTID','state_id',how='inner')
plot(state_geodata)

Visualización a nivel estado

tm_shape(mx_state_map) +
  tm_polygons(col = "black") +
  tm_compass(position = c("left", "bottom")) +
  tm_text("ADMIN_NAME", size = "AREA") +
  tm_layout(title = "Estados de México")

Ahora sí, mezclaremos la data de las geocercas con alguna de las variables que vienen en la tabla de datos inicial para su visualización en los mapas:

Distribución de Business Activity por Estado

businessactivity <- tm_shape(state_geodata) + 
  tm_polygons(
    col = "business_activity",
    palette = "-RdBu",  # el signo "-" invierte los colores para que los negativos estén en azul
    style = "cont",
    title = "Business Activity"
  ) +
  tm_layout(
    main.title = "Business Activity",
    title.position = c("right", "top"),
    legend.position = c("left", "bottom"),
    title.size = 1
  )

Distribución de Desempleo por Estado

unemployment <- tm_shape(state_geodata) + 
  tm_polygons(
    col = "unemployment",
    palette = "viridis",       # sin el "-" para que los valores más bajos sean claros
    style = "quantile",
    n = 8,
    title = "Unemployment"
  ) +
  tm_layout(
    main.title = "Unemployment",
    title.position = c("right", "top"),
    legend.position = c("left", "bottom"),
    title.size = 1
  )

Mapas creados previamente en un solo arreglo

tmap_arrange(businessactivity, unemployment, ncol = 2)

Matriz de Conectividad Espacial

swm  <- poly2nb(mx_state_map, queen=T)

summary(swm) # The average number of neighbors is 4.31
## Neighbour list object:
## Number of regions: 32 
## Number of nonzero links: 138 
## Percentage nonzero weights: 13.47656 
## Average number of links: 4.3125 
## Link number distribution:
## 
## 1 2 3 4 5 6 7 8 9 
## 1 6 6 6 5 2 3 2 1 
## 1 least connected region:
## 31 with 1 link
## 1 most connected region:
## 8 with 9 links

Visualización Gráfica de la matriz de conectividad (Vecindades estilo ‘Reina’)

sswm <- nb2listw(swm, style="W", zero.policy = TRUE)

mx_state_map_a <- as(mx_state_map, "Spatial")
mx_state_map_centroid <- coordinates(mx_state_map_a) 
plot(mx_state_map_a,border="blue",axes=FALSE,las=1, main="Mexico's States Queen SWM")
plot(mx_state_map_a,col="grey",border=grey(0.9),axes=T,add=T) 
plot(sswm,coords=mx_state_map_centroid,pch=19,cex=0.1,col="red",add=T) 

Análisis de Autocorrelación Espacial

Índice Global de Moran

moran.test(state_geodata$unemployment, sswm)            # Global Moran's I is 0.021 but not statistically significant (p-value > 10%). 
## 
##  Moran I test under randomisation
## 
## data:  state_geodata$unemployment  
## weights: sswm    
## 
## Moran I statistic standard deviate = 0.43866, p-value = 0.3305
## alternative hypothesis: greater
## sample estimates:
## Moran I statistic       Expectation          Variance 
##        0.02162698       -0.03225806        0.01508982
moran.test(state_geodata$business_activity, sswm)       # Global Moran's I is 0.18 but statistically significant (p-value > 10%). 
## 
##  Moran I test under randomisation
## 
## data:  state_geodata$business_activity  
## weights: sswm    
## 
## Moran I statistic standard deviate = 2.6024, p-value = 0.004629
## alternative hypothesis: greater
## sample estimates:
## Moran I statistic       Expectation          Variance 
##       0.184181268      -0.032258065       0.006917295

Presentación de resultados en formato tabla

table <- data.frame(Variable = c("Unemployment", "Business Activity"), GM = c(0.021, 0.18), Significance = c("NS","***"))
table
##            Variable    GM Significance
## 1      Unemployment 0.021           NS
## 2 Business Activity 0.180          ***

Identificación de Clusters

Cálculo de lags de acuerdo con la matriz de conectividad antes creada

state_geodata$sp_lag_unemployment <- lag.listw(sswm, state_geodata$unemployment, zero.policy=TRUE) 
state_geodata$sp_lag_business_activity <- lag.listw(sswm, state_geodata$business_activity, zero.policy=TRUE)

Mapas de lag para Business Activity y Unemployment

unemployment_lag <- tm_shape(state_geodata) + 
  tm_polygons(col = "sp_lag_unemployment", 
              palette="Greens", style="quantile", 
              n=8, 
              title="Unemployment") +
  tm_layout(main.title= 'Clusters of Unemployment',  
            title.position = c('right', 'top'), 
            legend.position= c("left", "bottom"), 
            title.size = 1)

ba_lag <- tm_shape(state_geodata) + 
  tm_polygons(
    col = "sp_lag_business_activity", 
    palette = "OrRd", 
    style = "quantile", 
    n = 8, 
    midpoint = NA,     # <-- evita forzar el punto medio en 0
    title = "Business Activity (Lag-1)"
  ) +
  tm_layout(
    main.title = "Clusters of Business Activity (Lag-1)",  
    title.position = c("right", "top"), 
    legend.position = c("left", "bottom"), 
    title.size = 1
  )
tmap_arrange(businessactivity, ba_lag, unemployment, unemployment_lag, ncol = 2)

Scatterplots

Business Activity

# Create a regression model
M1 <- lm(sp_lag_business_activity ~ business_activity, state_geodata)

# Plot the data
plot(sp_lag_business_activity ~ business_activity, state_geodata, pch=21, asp=1, las=1, col = "grey40", bg="grey80", main="Business Activity")
abline(M1, col="blue") # Add the regression line from model M
abline(v = mean(state_geodata$business_activity), lty=3, col = "grey80")
abline(h = mean(state_geodata$business_activity), lty=3, col = "grey80")

Unemployment

# Create a regression model
M2 <- lm(sp_lag_unemployment ~ unemployment, state_geodata)

# Plot the data
plot(sp_lag_unemployment ~ unemployment, state_geodata, pch=21, asp=1, las=1, col = "grey40", bg="grey80", main="Unemployment")
abline(M2, col="blue") # Add the regression line from model M
abline(v = mean(state_geodata$crime_rate), lty=3, col = "grey80")
abline(h = mean(state_geodata$crime_rate), lty=3, col = "grey80")

Visualización Espacial de Clusters (Conglomerados)

Segregación en las diferentes posibilidades de agrupaciones: + No significante + HotSpots: High - High + ColdSpots: Low - Low + Atípicos: High - Low + Atípicos: Low - High

sswm_a <- queen_weights(mx_state_map) # queen spatial weight matrix (alternative format)
lisa_income <- local_moran(sswm_a, state_geodata["unemployment"]) 
state_geodata$cluster_unemployment <- as.factor(lisa_income$GetClusterIndicators())
levels(state_geodata$cluster_unemployment)<-lisa_income$GetLabels() 

ggplot(data=state_geodata) +
  geom_sf(aes(fill=cluster_unemployment)) + 
  ggtitle(label = "Unemployment", subtitle = "Mexico's States")

Modelos

Cargamos algunas librerías adicionales que se utilizan para el ajuste de modelos de regresión:

library(spatialreg)
library(stargazer)

Regresión Simple

Primero recordemos los modelos de regresión tradicional (no espaciales)

model_a <- lm(new_fdi_real_mxn ~ business_activity + unemployment, data = state_geodata)
summary(model_a)
## 
## Call:
## lm(formula = new_fdi_real_mxn ~ business_activity + unemployment, 
##     data = state_geodata)
## 
## Residuals:
##    Min     1Q Median     3Q    Max 
##  -8473  -4523  -2443   1047  26134 
## 
## Coefficients:
##                   Estimate Std. Error t value Pr(>|t|)  
## (Intercept)          12176       4971   2.449   0.0206 *
## business_activity     4695       1722   2.727   0.0107 *
## unemployment         97524     124597   0.783   0.4401  
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 8077 on 29 degrees of freedom
## Multiple R-squared:  0.2043, Adjusted R-squared:  0.1494 
## F-statistic: 3.722 on 2 and 29 DF,  p-value: 0.03641
AIC(model_a)
## [1] 671.4531

Aquí se busca explicar la variable de Inversión Extranjera Directa considerando las variables de Actividad Empresarial, Promedio de Ingresos mensuales reales, Tasa de Criminalidad y Densidad Poblacional.

El modelo_a, aunque significativo, realmente no es tan bueno, pues la \(R^2\) ajustada apenas sobrepasa el 0.5 (\(\bar{R^2}\) = 0.5821).

Ahora buscaremos dar una mayor complejidad a los modelos previos al considerar terminos de autocorrelación espacial, es decir

SAR - Spatial AutoRegressive Model

También conocido como Spatial Lag Model

model_b <- lagsarlm(new_fdi_real_mxn ~ business_activity + unemployment, data = state_geodata, listw = sswm) 
summary(model_b)
## 
## Call:lagsarlm(formula = new_fdi_real_mxn ~ business_activity + unemployment, 
##     data = state_geodata, listw = sswm)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -8378.93 -4533.51 -2475.15   922.67 26266.71 
## 
## Type: lag 
## Coefficients: (numerical Hessian approximate standard errors) 
##                   Estimate Std. Error z value Pr(>|z|)
## (Intercept)        11811.5     5539.3  2.1323 0.032982
## business_activity   4623.7     1740.1  2.6572 0.007879
## unemployment       97513.3   118721.1  0.8214 0.411438
## 
## Rho: 0.032956, LR test value: 0.017546, p-value: 0.89462
## Approximate (numerical Hessian) standard error: 0.26295
##     z-value: 0.12534, p-value: 0.90026
## Wald statistic: 0.015709, p-value: 0.90026
## 
## Log likelihood: -331.7178 for lag model
## ML residual variance (sigma squared): 59069000, (sigma: 7685.6)
## Number of observations: 32 
## Number of parameters estimated: 5 
## AIC: 673.44, (AIC for lm: 671.45)
AIC(model_b)
## [1] 673.4356
# ?lagsarlm

Esencial, incluir la matriz de conectividad espacial dada en el parámetro ‘listw’ por el objeto previamente calculado con el nombre de ‘sswm’

SEM - Spatial Error Model

Modelo de Error Espacial

model_c <- errorsarlm(new_fdi_real_mxn ~ business_activity + unemployment, data = state_geodata, listw = sswm)
summary(model_c)
## 
## Call:errorsarlm(formula = new_fdi_real_mxn ~ business_activity + unemployment, 
##     data = state_geodata, listw = sswm)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -9664.0 -4067.2 -2083.9  1860.1 25175.2 
## 
## Type: error 
## Coefficients: (asymptotic standard errors) 
##                   Estimate Std. Error z value  Pr(>|z|)
## (Intercept)        13904.6     4153.0  3.3481 0.0008137
## business_activity   5786.5     1386.1  4.1746 2.985e-05
## unemployment      100452.6   110678.7  0.9076 0.3640865
## 
## Lambda: -0.39762, LR test value: 1.7915, p-value: 0.18074
## Asymptotic standard error: 0.23953
##     z-value: -1.66, p-value: 0.096917
## Wald statistic: 2.7556, p-value: 0.096917
## 
## Log likelihood: -330.8308 for error model
## ML residual variance (sigma squared): 53778000, (sigma: 7333.3)
## Number of observations: 32 
## Number of parameters estimated: 5 
## AIC: 671.66, (AIC for lm: 671.45)
AIC(model_c)
## [1] 671.6616
# ?errorsarlm

Igual que en el modelo SAR es esencial, incluir la matriz de conectividad espacial dada en el parámetro ‘listw’ por el objeto previamente calculado con el nombre de ‘sswm’

SDM - Spatial Durbin Model

model_d <- lagsarlm(new_fdi_real_mxn ~ business_activity + unemployment, data = state_geodata, listw = sswm, type="mixed") 
summary(model_d)
## 
## Call:lagsarlm(formula = new_fdi_real_mxn ~ business_activity + unemployment, 
##     data = state_geodata, listw = sswm, type = "mixed")
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -9395.9 -4069.3 -2000.3  2234.7 25838.6 
## 
## Type: mixed 
## Coefficients: (numerical Hessian approximate standard errors) 
##                       Estimate Std. Error z value Pr(>|z|)
## (Intercept)            16582.9    10206.0  1.6248 0.104201
## business_activity       4471.6     1664.1  2.6871 0.007208
## unemployment          105464.9   110826.1  0.9516 0.341287
## lag.business_activity   4018.2     2206.8  1.8208 0.068638
## lag.unemployment       97809.4   261432.2  0.3741 0.708308
## 
## Rho: -0.23256, LR test value: 0.6703, p-value: 0.41295
## Approximate (numerical Hessian) standard error: 0.28212
##     z-value: -0.82434, p-value: 0.40974
## Wald statistic: 0.67954, p-value: 0.40974
## 
## Log likelihood: -329.7602 for mixed model
## ML residual variance (sigma squared): 51591000, (sigma: 7182.7)
## Number of observations: 32 
## Number of parameters estimated: 7 
## AIC: 673.52, (AIC for lm: 672.19)
AIC(model_d)
## [1] 673.5205

Igual que en los modelo SAR y SEM es esencial, incluir la matriz de conectividad espacial dada en el parámetro ‘listw’ por el objeto previamente calculado con el nombre de ‘sswm’ y aquí en particular se debe añadir el parámetro ‘type’ con valor ‘mixed’ para que en esencia mezcle los dos modelos previos (SAR y SEM).

Comparativa

stargazer(model_a, model_b, model_c, model_d, type = "text", title="Estimated Regression Results")
## 
## Estimated Regression Results
## =======================================================================================
##                                              Dependent variable:                       
##                       -----------------------------------------------------------------
##                                               new_fdi_real_mxn                         
##                               OLS             spatial        spatial        spatial    
##                                            autoregressive     error      autoregressive
##                               (1)               (2)            (3)            (4)      
## ---------------------------------------------------------------------------------------
## business_activity         4,695.418**       4,623.750***   5,786.464***   4,471.587*** 
##                           (1,722.100)       (1,740.075)    (1,386.100)    (1,664.106)  
##                                                                                        
## unemployment               97,523.970        97,513.350    100,452.600    105,464.900  
##                          (124,597.400)     (118,721.100)  (110,678.700)  (110,826.100) 
##                                                                                        
## lag.business_activity                                                      4,018.200*  
##                                                                           (2,206.836)  
##                                                                                        
## lag.unemployment                                                           97,809.420  
##                                                                          (261,432.200) 
##                                                                                        
## Constant                  12,175.640**      11,811.500**  13,904.580***    16,582.930  
##                           (4,971.108)       (5,539.305)    (4,152.975)    (10,206.020) 
##                                                                                        
## ---------------------------------------------------------------------------------------
## Observations                   32                32             32             32      
## R2                           0.204                                                     
## Adjusted R2                  0.149                                                     
## Log Likelihood                                -331.718       -330.831       -329.760   
## sigma2                                     59,068,556.000 53,777,605.000 51,590,758.000
## Akaike Inf. Crit.                             673.436        671.662        673.520    
## Residual Std. Error   8,076.685 (df = 29)                                              
## F Statistic           3.722** (df = 2; 29)                                             
## Wald Test (df = 1)                             0.016          2.756*         0.680     
## LR Test (df = 1)                               0.018          1.792          0.670     
## =======================================================================================
## Note:                                                       *p<0.1; **p<0.05; ***p<0.01
LS0tDQp0aXRsZTogIkF1dG9jb3JyZWxhY2nDs24gRXNwYWNpYWwiDQpzdWJ0aXRsZTogIkFjdGl2aWRhZCAwMiINCmF1dGhvcjogIkVxdWlwbyA3Ig0KZGF0ZTogIjIwMjUtMDMtMjgiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZmxvYXQ6IHllcw0KICAgIGNvZGVfZG93bmxvYWQ6IHllcw0KICAgIHRoZW1lOiBjZXJ1bGVhbg0KICAgIGhpZ2hsaWdodDogcHlnbWVudHMNCi0tLQ0KDQohW10oaHR0cHM6Ly9jaXRyaXMtdWMub3JnL3dwLWNvbnRlbnQvdXBsb2Fkcy8yMDE5LzEwL1RlYy1kZS1Nb250ZXJyZXktbG9nby1ob3Jpem9udGFsLWJsdWUucG5nKQ0KDQo8ZGl2IHN0eWxlPSJ0ZXh0LWFsaWduOiBjZW50ZXIiPg0KICA8cD48c3Ryb25nPlBsYW5lYWNpw7NuIGVzdHJhdMOpZ2ljYSBiYXNhZGEgZW4gYW5hbMOtdGljYSBwcmVzY3JpcHRpdmE8L3N0cm9uZz48L3A+DQogIDxwPjxzdHJvbmc+R3J1cG8gNTAzPC9zdHJvbmc+PC9wPg0KICA8cD48c3Ryb25nPlByb2Zlc29yIFJvZG9sZm8gTWlndWVsIEdhbWVyb3M8L3N0cm9uZz48L3A+DQo8L2Rpdj4NCg0KPGRpdiBzdHlsZT0idGV4dC1hbGlnbjogY2VudGVyIj4NCiAgPHA+PHN0cm9uZz5FcXVpcG8gNzo8L3N0cm9uZz48L3A+DQogIDxwPkEwMDgzMzExMyAtIEF2cmlsIExvYmF0bzwvcD4NCiAgPHA+QTAxNzcxMTI3IC0gTGVzbHkgRGFyaWFuIFJvbWVybyBWw6F6cXVlejwvcD4NCiAgPHA+QTAwODMxMTA1IC0gSmF6bcOtbiBkZWwgQ2FybWVuIENvcnRleiBNZW5kb3phPC9wPg0KICA8cD5BMDEyODQ2MTEgLSBMaXNzZXQgSGVybsOhbmRlejwvcD4NCjwvZGl2Pg0KDQpgYGB7ciwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldCgNCgllY2hvID0gVFJVRSwNCgltZXNzYWdlID0gRkFMU0UsDQoJd2FybmluZyA9IEZBTFNFDQopDQojaW5zdGFsbC5wYWNrYWdlcygicm5hdHVyYWxlYXJ0aGhpcmVzIiwgcmVwb3MgPSAiaHR0cHM6Ly9wYWNrYWdlcy5yb3BlbnNjaS5vcmciKQ0KI2luc3RhbGwucGFja2FnZXMoImRldnRvb2xzIikNCnBhY21hbjo6cF9sb2FkKGRwbHlyLCBjYXIsIE1BU1MsIEFFUiwgcXVhbnRtb2QsIHN0YXRzLCBkbG9va3IsIHRpZHl2ZXJzZSwgdXJjYSwgYVRTQSwgZ2dwbG90MiwgbmFuaWFyLCB0aWdyaXMsIGNvcnJwbG90LCBwYXRjaHdvcmssIHJlYWR4bCwgcHN5Y2gsIG1hcHMsIHNmLCBybmF0dXJhbGVhcnRoLCBybmF0dXJhbGVhcnRoZGF0YSwgc3RyaW5naSwgc2NhbGVzLCBEYXRhRXhwbG9yZXIsIHRpZHlyLCBrbml0ciwgUkNvbG9yQnJld2VyLCBwbG90bHksIGZvcmVjYXN0LCB0c2VyaWVzLCBvcGVueGxzeCwgdmFycywgbG10ZXN0LCBmb3JlaWduLCBzcGRlcCwgcmdlb2RhLCB2aXJpZGlzLCB0bWFwLCBzcCwgc3Bnd3IsIEdXbW9kZWwsIGh0bWx0b29scykNCmBgYA0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkNCmBgYA0KDQoNCiMgKipBbsOhbGlzaXMgRXhwbG9yYXRvcmlvIGRlIERhdG9zIEVzcGFjaWFsZXMgKEVTREE6IEV4cGxvcmF0b3J5IFNwYXRpYWwgRGF0YSBBbmFseXNpcykqKg0KDQpFbCBBbsOhbGlzaXMgZXhwbG9yYXRvcmlvIGRlIGRhdG9zIGVzcGFjaWFsIGludm9sdWNyYSBkaWZlcmVudGVzIGhlcnJhbWllbnRhcyBwYXJhIHZpc3VhbGl6YXIgbG9zIHBhdHJvbmVzIGVzcGFjaWFsZXMgZXhpc3RlbnRlcyBlbiBsYSBiYXNlIGRlIGRhdG9zLiBFc3RvcyBkYXRvcyBzZSBwdWVkZW4gc2VncmVnYXIgbyBkZXNjb21wb25lciBsb3MgcGF0cm9uZXMgZXNwYWNpYWxlcyBlbiBkaWZlcmVudGVzIGVsZW1lbnRvcyBhIGxvIGxhcmdvIGRlcyBtYXBhLCB5IGEgc3UgdmV6IG1lZGlyIGxhIGV4dGVuc2nDs24gZGUgZXN0YXMgYXV0b2NvcnJlbGFjaW9uZXMgZXNwYWNpYWxlcy4gRWwgcHJpbmNpcGFsIHBhcXVldGUgZGUgUiBxdWUgc2UgdXRpbGl6YSBwYXJhIGVzdGUgcHJvcMOzc2l0byBlcyAnZ2Vvc3RhbicuDQoNCiMjIEltcG9ydGFjacOzbiBkZSBiYXNlcyBkZSBkYXRvcyB5IGdlb2NlcmNhcw0KDQpBIG5pdmVsIGVzdGF0YWwNCmBgYHtyfQ0KbXhfc3RhdGUgPC0gcmVhZC5jc3YoInN0YXRlX2RhdGEuY3N2IikNCmhlYWQobXhfc3RhdGUpDQpnbGltcHNlKG14X3N0YXRlKQ0KYGBgDQoNClNlZ3VpZGFtZW50ZSwgc2UgbGVlbiBsb3MgYXJjaGl2b3MgZGUgbGFzIGZvcm1hcyBnZW9tw6l0cmljYXMgKGdlb2NlcmNhcykgcXVlIGRldGVybWluYXLDoW4gbG9zIG1hcGFzIGEgcmVhbGl6YXIgZGVzcHXDqXMgcGFyYSB2aXN1YWxpemFyIGxvcyBkYXRvcy4NCg0KQSBuaXZlbCBlc3RhZG8gKDMyKQ0KYGBge3J9DQpteF9zdGF0ZV9tYXAgPC0gc3RfcmVhZCgibWV4bGF0bG9uZy5zaHAiKQ0KI214X3N0YXRlX21hcCA8LSByZWFkX3NmKCJtZXhsYXRsb25nLnNocCIpDQpgYGANCg0KDQojIyBCYXNpYyBNYXAgTWFraW5nDQoNCmBgYHtyfQ0KIyMjIEJhc2ljIE1hcCBNYWtpbmcNCnN0YXRlX2dlb2RhdGEgPC0gZ2VvX2pvaW4obXhfc3RhdGVfbWFwLG14X3N0YXRlLCdPQkpFQ1RJRCcsJ3N0YXRlX2lkJyxob3c9J2lubmVyJykNCnBsb3Qoc3RhdGVfZ2VvZGF0YSkNCmBgYA0KDQoNCiMjIFZpc3VhbGl6YWNpw7NuIGEgbml2ZWwgZXN0YWRvDQpgYGB7cn0NCnRtX3NoYXBlKG14X3N0YXRlX21hcCkgKw0KICB0bV9wb2x5Z29ucyhjb2wgPSAiYmxhY2siKSArDQogIHRtX2NvbXBhc3MocG9zaXRpb24gPSBjKCJsZWZ0IiwgImJvdHRvbSIpKSArDQogIHRtX3RleHQoIkFETUlOX05BTUUiLCBzaXplID0gIkFSRUEiKSArDQogIHRtX2xheW91dCh0aXRsZSA9ICJFc3RhZG9zIGRlIE3DqXhpY28iKQ0KYGBgDQoNCkFob3JhIHPDrSwgbWV6Y2xhcmVtb3MgbGEgZGF0YSBkZSBsYXMgZ2VvY2VyY2FzIGNvbiBhbGd1bmEgZGUgbGFzIHZhcmlhYmxlcyBxdWUgdmllbmVuIGVuIGxhIHRhYmxhIGRlIGRhdG9zIGluaWNpYWwgcGFyYSBzdSB2aXN1YWxpemFjacOzbiBlbiBsb3MgbWFwYXM6DQoNCiMjIERpc3RyaWJ1Y2nDs24gZGUgQnVzaW5lc3MgQWN0aXZpdHkgcG9yIEVzdGFkbw0KYGBge3J9DQpidXNpbmVzc2FjdGl2aXR5IDwtIHRtX3NoYXBlKHN0YXRlX2dlb2RhdGEpICsgDQogIHRtX3BvbHlnb25zKA0KICAgIGNvbCA9ICJidXNpbmVzc19hY3Rpdml0eSIsDQogICAgcGFsZXR0ZSA9ICItUmRCdSIsICAjIGVsIHNpZ25vICItIiBpbnZpZXJ0ZSBsb3MgY29sb3JlcyBwYXJhIHF1ZSBsb3MgbmVnYXRpdm9zIGVzdMOpbiBlbiBhenVsDQogICAgc3R5bGUgPSAiY29udCIsDQogICAgdGl0bGUgPSAiQnVzaW5lc3MgQWN0aXZpdHkiDQogICkgKw0KICB0bV9sYXlvdXQoDQogICAgbWFpbi50aXRsZSA9ICJCdXNpbmVzcyBBY3Rpdml0eSIsDQogICAgdGl0bGUucG9zaXRpb24gPSBjKCJyaWdodCIsICJ0b3AiKSwNCiAgICBsZWdlbmQucG9zaXRpb24gPSBjKCJsZWZ0IiwgImJvdHRvbSIpLA0KICAgIHRpdGxlLnNpemUgPSAxDQogICkNCmBgYA0KDQojIyBEaXN0cmlidWNpw7NuIGRlIERlc2VtcGxlbyBwb3IgRXN0YWRvDQpgYGB7cn0NCnVuZW1wbG95bWVudCA8LSB0bV9zaGFwZShzdGF0ZV9nZW9kYXRhKSArIA0KICB0bV9wb2x5Z29ucygNCiAgICBjb2wgPSAidW5lbXBsb3ltZW50IiwNCiAgICBwYWxldHRlID0gInZpcmlkaXMiLCAgICAgICAjIHNpbiBlbCAiLSIgcGFyYSBxdWUgbG9zIHZhbG9yZXMgbcOhcyBiYWpvcyBzZWFuIGNsYXJvcw0KICAgIHN0eWxlID0gInF1YW50aWxlIiwNCiAgICBuID0gOCwNCiAgICB0aXRsZSA9ICJVbmVtcGxveW1lbnQiDQogICkgKw0KICB0bV9sYXlvdXQoDQogICAgbWFpbi50aXRsZSA9ICJVbmVtcGxveW1lbnQiLA0KICAgIHRpdGxlLnBvc2l0aW9uID0gYygicmlnaHQiLCAidG9wIiksDQogICAgbGVnZW5kLnBvc2l0aW9uID0gYygibGVmdCIsICJib3R0b20iKSwNCiAgICB0aXRsZS5zaXplID0gMQ0KICApDQpgYGANCg0KIyMgTWFwYXMgY3JlYWRvcyBwcmV2aWFtZW50ZSBlbiB1biBzb2xvIGFycmVnbG8NCmBgYHtyfQ0KdG1hcF9hcnJhbmdlKGJ1c2luZXNzYWN0aXZpdHksIHVuZW1wbG95bWVudCwgbmNvbCA9IDIpDQpgYGANCg0KDQojIyBNYXRyaXogZGUgQ29uZWN0aXZpZGFkIEVzcGFjaWFsDQpgYGB7cn0NCnN3bSAgPC0gcG9seTJuYihteF9zdGF0ZV9tYXAsIHF1ZWVuPVQpDQoNCnN1bW1hcnkoc3dtKSAjIFRoZSBhdmVyYWdlIG51bWJlciBvZiBuZWlnaGJvcnMgaXMgNC4zMQ0KYGBgDQoNCiMjIFZpc3VhbGl6YWNpw7NuIEdyw6FmaWNhIGRlIGxhIG1hdHJpeiBkZSBjb25lY3RpdmlkYWQgKFZlY2luZGFkZXMgZXN0aWxvICdSZWluYScpDQpgYGB7cn0NCnNzd20gPC0gbmIybGlzdHcoc3dtLCBzdHlsZT0iVyIsIHplcm8ucG9saWN5ID0gVFJVRSkNCg0KbXhfc3RhdGVfbWFwX2EgPC0gYXMobXhfc3RhdGVfbWFwLCAiU3BhdGlhbCIpDQpteF9zdGF0ZV9tYXBfY2VudHJvaWQgPC0gY29vcmRpbmF0ZXMobXhfc3RhdGVfbWFwX2EpIA0KcGxvdChteF9zdGF0ZV9tYXBfYSxib3JkZXI9ImJsdWUiLGF4ZXM9RkFMU0UsbGFzPTEsIG1haW49Ik1leGljbydzIFN0YXRlcyBRdWVlbiBTV00iKQ0KcGxvdChteF9zdGF0ZV9tYXBfYSxjb2w9ImdyZXkiLGJvcmRlcj1ncmV5KDAuOSksYXhlcz1ULGFkZD1UKSANCnBsb3Qoc3N3bSxjb29yZHM9bXhfc3RhdGVfbWFwX2NlbnRyb2lkLHBjaD0xOSxjZXg9MC4xLGNvbD0icmVkIixhZGQ9VCkgDQpgYGANCg0KDQojICoqQW7DoWxpc2lzIGRlIEF1dG9jb3JyZWxhY2nDs24gRXNwYWNpYWwqKg0KDQojIyDDjW5kaWNlIEdsb2JhbCBkZSBNb3Jhbg0KYGBge3J9DQptb3Jhbi50ZXN0KHN0YXRlX2dlb2RhdGEkdW5lbXBsb3ltZW50LCBzc3dtKSAgICAgICAgICAgICMgR2xvYmFsIE1vcmFuJ3MgSSBpcyAwLjAyMSBidXQgbm90IHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgKHAtdmFsdWUgPiAxMCUpLiANCm1vcmFuLnRlc3Qoc3RhdGVfZ2VvZGF0YSRidXNpbmVzc19hY3Rpdml0eSwgc3N3bSkgICAgICAgIyBHbG9iYWwgTW9yYW4ncyBJIGlzIDAuMTggYnV0IHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgKHAtdmFsdWUgPiAxMCUpLiANCmBgYA0KDQpQcmVzZW50YWNpw7NuIGRlIHJlc3VsdGFkb3MgZW4gZm9ybWF0byB0YWJsYQ0KYGBge3J9DQp0YWJsZSA8LSBkYXRhLmZyYW1lKFZhcmlhYmxlID0gYygiVW5lbXBsb3ltZW50IiwgIkJ1c2luZXNzIEFjdGl2aXR5IiksIEdNID0gYygwLjAyMSwgMC4xOCksIFNpZ25pZmljYW5jZSA9IGMoIk5TIiwiKioqIikpDQp0YWJsZQ0KYGBgDQoNCg0KIyMgSWRlbnRpZmljYWNpw7NuIGRlIENsdXN0ZXJzDQoNCkPDoWxjdWxvIGRlIGxhZ3MgZGUgYWN1ZXJkbyBjb24gbGEgbWF0cml6IGRlIGNvbmVjdGl2aWRhZCBhbnRlcyBjcmVhZGENCmBgYHtyfQ0Kc3RhdGVfZ2VvZGF0YSRzcF9sYWdfdW5lbXBsb3ltZW50IDwtIGxhZy5saXN0dyhzc3dtLCBzdGF0ZV9nZW9kYXRhJHVuZW1wbG95bWVudCwgemVyby5wb2xpY3k9VFJVRSkgDQpzdGF0ZV9nZW9kYXRhJHNwX2xhZ19idXNpbmVzc19hY3Rpdml0eSA8LSBsYWcubGlzdHcoc3N3bSwgc3RhdGVfZ2VvZGF0YSRidXNpbmVzc19hY3Rpdml0eSwgemVyby5wb2xpY3k9VFJVRSkNCmBgYA0KDQojIyMgTWFwYXMgZGUgbGFnIHBhcmEgQnVzaW5lc3MgQWN0aXZpdHkgeSBVbmVtcGxveW1lbnQNCmBgYHtyfQ0KdW5lbXBsb3ltZW50X2xhZyA8LSB0bV9zaGFwZShzdGF0ZV9nZW9kYXRhKSArIA0KICB0bV9wb2x5Z29ucyhjb2wgPSAic3BfbGFnX3VuZW1wbG95bWVudCIsIA0KICAgICAgICAgICAgICBwYWxldHRlPSJHcmVlbnMiLCBzdHlsZT0icXVhbnRpbGUiLCANCiAgICAgICAgICAgICAgbj04LCANCiAgICAgICAgICAgICAgdGl0bGU9IlVuZW1wbG95bWVudCIpICsNCiAgdG1fbGF5b3V0KG1haW4udGl0bGU9ICdDbHVzdGVycyBvZiBVbmVtcGxveW1lbnQnLCAgDQogICAgICAgICAgICB0aXRsZS5wb3NpdGlvbiA9IGMoJ3JpZ2h0JywgJ3RvcCcpLCANCiAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbj0gYygibGVmdCIsICJib3R0b20iKSwgDQogICAgICAgICAgICB0aXRsZS5zaXplID0gMSkNCg0KYmFfbGFnIDwtIHRtX3NoYXBlKHN0YXRlX2dlb2RhdGEpICsgDQogIHRtX3BvbHlnb25zKA0KICAgIGNvbCA9ICJzcF9sYWdfYnVzaW5lc3NfYWN0aXZpdHkiLCANCiAgICBwYWxldHRlID0gIk9yUmQiLCANCiAgICBzdHlsZSA9ICJxdWFudGlsZSIsIA0KICAgIG4gPSA4LCANCiAgICBtaWRwb2ludCA9IE5BLCAgICAgIyA8LS0gZXZpdGEgZm9yemFyIGVsIHB1bnRvIG1lZGlvIGVuIDANCiAgICB0aXRsZSA9ICJCdXNpbmVzcyBBY3Rpdml0eSAoTGFnLTEpIg0KICApICsNCiAgdG1fbGF5b3V0KA0KICAgIG1haW4udGl0bGUgPSAiQ2x1c3RlcnMgb2YgQnVzaW5lc3MgQWN0aXZpdHkgKExhZy0xKSIsICANCiAgICB0aXRsZS5wb3NpdGlvbiA9IGMoInJpZ2h0IiwgInRvcCIpLCANCiAgICBsZWdlbmQucG9zaXRpb24gPSBjKCJsZWZ0IiwgImJvdHRvbSIpLCANCiAgICB0aXRsZS5zaXplID0gMQ0KICApDQp0bWFwX2FycmFuZ2UoYnVzaW5lc3NhY3Rpdml0eSwgYmFfbGFnLCB1bmVtcGxveW1lbnQsIHVuZW1wbG95bWVudF9sYWcsIG5jb2wgPSAyKQ0KYGBgDQoNCg0KIyMgKipTY2F0dGVycGxvdHMqKg0KDQojIyMgQnVzaW5lc3MgQWN0aXZpdHkNCg0KYGBge3J9DQojIENyZWF0ZSBhIHJlZ3Jlc3Npb24gbW9kZWwNCk0xIDwtIGxtKHNwX2xhZ19idXNpbmVzc19hY3Rpdml0eSB+IGJ1c2luZXNzX2FjdGl2aXR5LCBzdGF0ZV9nZW9kYXRhKQ0KDQojIFBsb3QgdGhlIGRhdGENCnBsb3Qoc3BfbGFnX2J1c2luZXNzX2FjdGl2aXR5IH4gYnVzaW5lc3NfYWN0aXZpdHksIHN0YXRlX2dlb2RhdGEsIHBjaD0yMSwgYXNwPTEsIGxhcz0xLCBjb2wgPSAiZ3JleTQwIiwgYmc9ImdyZXk4MCIsIG1haW49IkJ1c2luZXNzIEFjdGl2aXR5IikNCmFibGluZShNMSwgY29sPSJibHVlIikgIyBBZGQgdGhlIHJlZ3Jlc3Npb24gbGluZSBmcm9tIG1vZGVsIE0NCmFibGluZSh2ID0gbWVhbihzdGF0ZV9nZW9kYXRhJGJ1c2luZXNzX2FjdGl2aXR5KSwgbHR5PTMsIGNvbCA9ICJncmV5ODAiKQ0KYWJsaW5lKGggPSBtZWFuKHN0YXRlX2dlb2RhdGEkYnVzaW5lc3NfYWN0aXZpdHkpLCBsdHk9MywgY29sID0gImdyZXk4MCIpDQpgYGANCg0KIyMjIFVuZW1wbG95bWVudA0KYGBge3J9DQojIENyZWF0ZSBhIHJlZ3Jlc3Npb24gbW9kZWwNCk0yIDwtIGxtKHNwX2xhZ191bmVtcGxveW1lbnQgfiB1bmVtcGxveW1lbnQsIHN0YXRlX2dlb2RhdGEpDQoNCiMgUGxvdCB0aGUgZGF0YQ0KcGxvdChzcF9sYWdfdW5lbXBsb3ltZW50IH4gdW5lbXBsb3ltZW50LCBzdGF0ZV9nZW9kYXRhLCBwY2g9MjEsIGFzcD0xLCBsYXM9MSwgY29sID0gImdyZXk0MCIsIGJnPSJncmV5ODAiLCBtYWluPSJVbmVtcGxveW1lbnQiKQ0KYWJsaW5lKE0yLCBjb2w9ImJsdWUiKSAjIEFkZCB0aGUgcmVncmVzc2lvbiBsaW5lIGZyb20gbW9kZWwgTQ0KYWJsaW5lKHYgPSBtZWFuKHN0YXRlX2dlb2RhdGEkY3JpbWVfcmF0ZSksIGx0eT0zLCBjb2wgPSAiZ3JleTgwIikNCmFibGluZShoID0gbWVhbihzdGF0ZV9nZW9kYXRhJGNyaW1lX3JhdGUpLCBsdHk9MywgY29sID0gImdyZXk4MCIpDQpgYGANCg0KIyMgVmlzdWFsaXphY2nDs24gRXNwYWNpYWwgZGUgQ2x1c3RlcnMgKENvbmdsb21lcmFkb3MpDQoNClNlZ3JlZ2FjacOzbiBlbiBsYXMgZGlmZXJlbnRlcyBwb3NpYmlsaWRhZGVzIGRlIGFncnVwYWNpb25lczoNCisgTm8gc2lnbmlmaWNhbnRlDQorIEhvdFNwb3RzOiBIaWdoIC0gSGlnaA0KKyBDb2xkU3BvdHM6IExvdyAtIExvdw0KKyBBdMOtcGljb3M6IEhpZ2ggLSBMb3cNCisgQXTDrXBpY29zOiBMb3cgLSBIaWdoDQoNCmBgYHtyfQ0Kc3N3bV9hIDwtIHF1ZWVuX3dlaWdodHMobXhfc3RhdGVfbWFwKSAjIHF1ZWVuIHNwYXRpYWwgd2VpZ2h0IG1hdHJpeCAoYWx0ZXJuYXRpdmUgZm9ybWF0KQ0KbGlzYV9pbmNvbWUgPC0gbG9jYWxfbW9yYW4oc3N3bV9hLCBzdGF0ZV9nZW9kYXRhWyJ1bmVtcGxveW1lbnQiXSkgDQpzdGF0ZV9nZW9kYXRhJGNsdXN0ZXJfdW5lbXBsb3ltZW50IDwtIGFzLmZhY3RvcihsaXNhX2luY29tZSRHZXRDbHVzdGVySW5kaWNhdG9ycygpKQ0KbGV2ZWxzKHN0YXRlX2dlb2RhdGEkY2x1c3Rlcl91bmVtcGxveW1lbnQpPC1saXNhX2luY29tZSRHZXRMYWJlbHMoKSANCg0KZ2dwbG90KGRhdGE9c3RhdGVfZ2VvZGF0YSkgKw0KICBnZW9tX3NmKGFlcyhmaWxsPWNsdXN0ZXJfdW5lbXBsb3ltZW50KSkgKyANCiAgZ2d0aXRsZShsYWJlbCA9ICJVbmVtcGxveW1lbnQiLCBzdWJ0aXRsZSA9ICJNZXhpY28ncyBTdGF0ZXMiKQ0KYGBgDQoNCg0KIyAqKk1vZGVsb3MqKg0KDQpDYXJnYW1vcyBhbGd1bmFzIGxpYnJlcsOtYXMgYWRpY2lvbmFsZXMgcXVlIHNlIHV0aWxpemFuIHBhcmEgZWwgYWp1c3RlIGRlIG1vZGVsb3MgZGUgcmVncmVzacOzbjoNCmBgYHtyfQ0KbGlicmFyeShzcGF0aWFscmVnKQ0KbGlicmFyeShzdGFyZ2F6ZXIpDQpgYGANCg0KDQojIyBSZWdyZXNpw7NuIFNpbXBsZQ0KUHJpbWVybyByZWNvcmRlbW9zIGxvcyBtb2RlbG9zIGRlIHJlZ3Jlc2nDs24gdHJhZGljaW9uYWwgKG5vIGVzcGFjaWFsZXMpDQpgYGB7cn0NCm1vZGVsX2EgPC0gbG0obmV3X2ZkaV9yZWFsX214biB+IGJ1c2luZXNzX2FjdGl2aXR5ICsgdW5lbXBsb3ltZW50LCBkYXRhID0gc3RhdGVfZ2VvZGF0YSkNCnN1bW1hcnkobW9kZWxfYSkNCkFJQyhtb2RlbF9hKQ0KYGBgDQoNCkFxdcOtIHNlIGJ1c2NhIGV4cGxpY2FyIGxhIHZhcmlhYmxlIGRlIEludmVyc2nDs24gRXh0cmFuamVyYSBEaXJlY3RhIGNvbnNpZGVyYW5kbyBsYXMgdmFyaWFibGVzIGRlIEFjdGl2aWRhZCBFbXByZXNhcmlhbCwgUHJvbWVkaW8gZGUgSW5ncmVzb3MgbWVuc3VhbGVzIHJlYWxlcywgVGFzYSBkZSBDcmltaW5hbGlkYWQgeSBEZW5zaWRhZCBQb2JsYWNpb25hbC4NCg0KRWwgbW9kZWxvX2EsIGF1bnF1ZSBzaWduaWZpY2F0aXZvLCByZWFsbWVudGUgbm8gZXMgdGFuIGJ1ZW5vLCBwdWVzIGxhICRSXjIkIGFqdXN0YWRhIGFwZW5hcyBzb2JyZXBhc2EgZWwgMC41ICgkXGJhcntSXjJ9JCA9IDAuNTgyMSkuIA0KDQoNCkFob3JhIGJ1c2NhcmVtb3MgZGFyIHVuYSBtYXlvciBjb21wbGVqaWRhZCBhIGxvcyBtb2RlbG9zIHByZXZpb3MgYWwgY29uc2lkZXJhciB0ZXJtaW5vcyBkZSBhdXRvY29ycmVsYWNpw7NuIGVzcGFjaWFsLCBlcyBkZWNpcg0KDQojIyBTQVIgLSBTcGF0aWFsIEF1dG9SZWdyZXNzaXZlIE1vZGVsDQoNClRhbWJpw6luIGNvbm9jaWRvIGNvbW8gU3BhdGlhbCBMYWcgTW9kZWwNCmBgYHtyfQ0KbW9kZWxfYiA8LSBsYWdzYXJsbShuZXdfZmRpX3JlYWxfbXhuIH4gYnVzaW5lc3NfYWN0aXZpdHkgKyB1bmVtcGxveW1lbnQsIGRhdGEgPSBzdGF0ZV9nZW9kYXRhLCBsaXN0dyA9IHNzd20pIA0Kc3VtbWFyeShtb2RlbF9iKQ0KQUlDKG1vZGVsX2IpDQojID9sYWdzYXJsbQ0KYGBgDQoNCkVzZW5jaWFsLCBpbmNsdWlyIGxhIG1hdHJpeiBkZSBjb25lY3RpdmlkYWQgZXNwYWNpYWwgZGFkYSBlbiBlbCBwYXLDoW1ldHJvICdsaXN0dycgcG9yIGVsIG9iamV0byBwcmV2aWFtZW50ZSBjYWxjdWxhZG8gY29uIGVsIG5vbWJyZSBkZSAnc3N3bScNCg0KIyMgU0VNIC0gU3BhdGlhbCBFcnJvciBNb2RlbA0KDQpNb2RlbG8gZGUgRXJyb3IgRXNwYWNpYWwNCmBgYHtyfQ0KbW9kZWxfYyA8LSBlcnJvcnNhcmxtKG5ld19mZGlfcmVhbF9teG4gfiBidXNpbmVzc19hY3Rpdml0eSArIHVuZW1wbG95bWVudCwgZGF0YSA9IHN0YXRlX2dlb2RhdGEsIGxpc3R3ID0gc3N3bSkNCnN1bW1hcnkobW9kZWxfYykNCkFJQyhtb2RlbF9jKQ0KIyA/ZXJyb3JzYXJsbQ0KYGBgDQoNCklndWFsIHF1ZSBlbiBlbCBtb2RlbG8gU0FSIGVzIGVzZW5jaWFsLCBpbmNsdWlyIGxhIG1hdHJpeiBkZSBjb25lY3RpdmlkYWQgZXNwYWNpYWwgZGFkYSBlbiBlbCBwYXLDoW1ldHJvICdsaXN0dycgcG9yIGVsIG9iamV0byBwcmV2aWFtZW50ZSBjYWxjdWxhZG8gY29uIGVsIG5vbWJyZSBkZSAnc3N3bScNCg0KDQojIyBTRE0gLSAgU3BhdGlhbCBEdXJiaW4gTW9kZWwNCg0KYGBge3J9DQptb2RlbF9kIDwtIGxhZ3NhcmxtKG5ld19mZGlfcmVhbF9teG4gfiBidXNpbmVzc19hY3Rpdml0eSArIHVuZW1wbG95bWVudCwgZGF0YSA9IHN0YXRlX2dlb2RhdGEsIGxpc3R3ID0gc3N3bSwgdHlwZT0ibWl4ZWQiKSANCnN1bW1hcnkobW9kZWxfZCkNCkFJQyhtb2RlbF9kKQ0KYGBgDQoNCklndWFsIHF1ZSBlbiBsb3MgbW9kZWxvIFNBUiB5IFNFTSBlcyBlc2VuY2lhbCwgaW5jbHVpciBsYSBtYXRyaXogZGUgY29uZWN0aXZpZGFkIGVzcGFjaWFsIGRhZGEgZW4gZWwgcGFyw6FtZXRybyAnbGlzdHcnIHBvciBlbCBvYmpldG8gcHJldmlhbWVudGUgY2FsY3VsYWRvIGNvbiBlbCBub21icmUgZGUgJ3Nzd20nIHkgYXF1w60gZW4gcGFydGljdWxhciBzZSBkZWJlIGHDsWFkaXIgZWwgcGFyw6FtZXRybyAndHlwZScgY29uIHZhbG9yICdtaXhlZCcgcGFyYSBxdWUgZW4gZXNlbmNpYSBtZXpjbGUgbG9zIGRvcyBtb2RlbG9zIHByZXZpb3MgKFNBUiB5IFNFTSkuDQoNCiMjIENvbXBhcmF0aXZhDQpgYGB7cn0NCnN0YXJnYXplcihtb2RlbF9hLCBtb2RlbF9iLCBtb2RlbF9jLCBtb2RlbF9kLCB0eXBlID0gInRleHQiLCB0aXRsZT0iRXN0aW1hdGVkIFJlZ3Jlc3Npb24gUmVzdWx0cyIpDQpgYGANCg0KIyAqKlJlZmVyZW5jaWFzKioNCg0KV2hhdCBpcyBleHBsb3JhdG9yeSBkYXRhIGFuYWx5c2lzPyANCmh0dHBzOi8vd3d3LmlibS5jb20vdG9waWNzL2V4cGxvcmF0b3J5LWRhdGEtYW5hbHlzaXMNCg0KRXhwbG9yYXRvcnkgU3BhdGlhbCBEYXRhIEFuYWx5c2lzDQpodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvZ2Vvc3Rhbi92aWduZXR0ZXMvbWVhc3VyaW5nLXNhLmh0bWwNCg0K