Contexto

La base de datos USArrests contiene estadísticas en arrestos por cada 100,000 residentes por agresión, asesinato y violación en cada uno de los 50 estados de EE.UU. en 1973.

Instalar paquetes y llamar librerías

#install.packages("cluster") #Para agrupamiento de datos 
library(cluster)
#install.packages("ggplot2") #Gráficas
library(ggplot2)
#install.packages("factoextra") #Visualizar clusters
library(factoextra)
#install.packages("data.table") #Conjunto de datos grande 
library(data.table)
#install.packages("tidyverse")
library(tidyverse)
#install.packages("caret") # Algoritmos de aprendizaje automático
library(caret)
#install.packages("datasets") # Para usar base de datos "iris"
library(datasets)
#install.packages("ggplot2") # Graficas con mejor diseño
library(ggplot2)
#install.packages("lattice") # Crear gráficos
library(lattice)
#install.packages("DataExplorer") # Análisis Descriptivo
library(DataExplorer)
#install.packages("kernlab")
library(kernlab)
#install.packages("randomForest")
library(randomForest)
library(tibble)
#install.packages("sf") #Analisis de datos espaciales
library(sf)
#install.packages("rnaturalearth") #Proporciona límites geograficos
library(rnaturalearth)
#install.packages("rnaturalearthdata") #Datos de geografia
library(rnaturalearthdata)
#install.packages("devtools")
library(devtools)
devtools::install_github("ropensci/rnaturalearthhires") #Mapa de México particular

Importar base de datos

df <- USArrests

Análisis descriptivo

create_report(df)
##   |                                             |                                     |   0%  |                                             |.                                    |   2%                                   |                                             |..                                   |   5% [global_options]                  |                                             |...                                  |   7%                                   |                                             |....                                 |  10% [introduce]                       |                                             |....                                 |  12%                                   |                                             |.....                                |  14% [plot_intro]
##   |                                             |......                               |  17%                                   |                                             |.......                              |  19% [data_structure]                  |                                             |........                             |  21%                                   |                                             |.........                            |  24% [missing_profile]
##   |                                             |..........                           |  26%                                   |                                             |...........                          |  29% [univariate_distribution_header]  |                                             |...........                          |  31%                                   |                                             |............                         |  33% [plot_histogram]
##   |                                             |.............                        |  36%                                   |                                             |..............                       |  38% [plot_density]                    |                                             |...............                      |  40%                                   |                                             |................                     |  43% [plot_frequency_bar]              |                                             |.................                    |  45%                                   |                                             |..................                   |  48% [plot_response_bar]               |                                             |..................                   |  50%                                   |                                             |...................                  |  52% [plot_with_bar]                   |                                             |....................                 |  55%                                   |                                             |.....................                |  57% [plot_normal_qq]
##   |                                             |......................               |  60%                                   |                                             |.......................              |  62% [plot_response_qq]                |                                             |........................             |  64%                                   |                                             |.........................            |  67% [plot_by_qq]                      |                                             |..........................           |  69%                                   |                                             |..........................           |  71% [correlation_analysis]
##   |                                             |...........................          |  74%                                   |                                             |............................         |  76% [principal_component_analysis]
##   |                                             |.............................        |  79%                                   |                                             |..............................       |  81% [bivariate_distribution_header]   |                                             |...............................      |  83%                                   |                                             |................................     |  86% [plot_response_boxplot]           |                                             |.................................    |  88%                                   |                                             |.................................    |  90% [plot_by_boxplot]                 |                                             |..................................   |  93%                                   |                                             |...................................  |  95% [plot_response_scatterplot]       |                                             |.................................... |  98%                                   |                                             |.....................................| 100% [plot_by_scatterplot]           
## /Applications/RStudio.app/Contents/Resources/app/quarto/bin/tools/pandoc +RTS -K512m -RTS /Users/mariadelbosque/Desktop/TEC/CONCENTRACION/R/report.knit.md --to html4 --from markdown+autolink_bare_uris+tex_math_single_backslash --output /Users/mariadelbosque/Desktop/TEC/CONCENTRACION/R/report.html --lua-filter /Library/Frameworks/R.framework/Versions/4.4-x86_64/Resources/library/rmarkdown/rmarkdown/lua/pagebreak.lua --lua-filter /Library/Frameworks/R.framework/Versions/4.4-x86_64/Resources/library/rmarkdown/rmarkdown/lua/latex-div.lua --embed-resources --standalone --variable bs3=TRUE --section-divs --table-of-contents --toc-depth 6 --template /Library/Frameworks/R.framework/Versions/4.4-x86_64/Resources/library/rmarkdown/rmd/h/default.html --no-highlight --variable highlightjs=1 --variable theme=yeti --mathjax --variable 'mathjax-url=https://mathjax.rstudio.com/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML' --include-in-header /var/folders/fg/qsq_j5fx5wz0c1xt7brrw6fh0000gn/T//Rtmpa85o7o/rmarkdown-str36e61de02cfb.html
plot_missing(df)

plot_histogram(df)

plot_correlation(df)

# Clusters

Escalar la base de datos

df_escalado <-scale(df)
df_escalado <- subset(df_escalado,select = -UrbanPop)
df_escalado <-scale(df_escalado)

Generar los segmentos

grupos <- 4 #Inicio con cualquier valor y luego verifico
segmentos <- kmeans(df_escalado, grupos)

Asignar grupos a los datos

asignacion <- cbind(df, cluster=segmentos$cluster)

Graficar los clusters

fviz_cluster(segmentos, data = df)

## Optimizar la cantidad de grupos

#La cantidad óptima de grupos corresponde al primer punto más alto de la gráfica
set.seed(123)
#optimizacion <- clusGap(df_escalado, FUN=kmeans, nstart=1, K.max =10)
#plot(optimizacion, xlab = "Número de Clusters k")

# Crear una función para calcular la suma de los cuadrados dentro del grupo (WCSS)
wcss <- function(k) {
  kmeans(df_escalado, centers = k, nstart = 10)$tot.withinss
}

# Evaluar WCSS para diferentes valores de k
k_values <- 1:10
wcss_values <- sapply(k_values, wcss)

# Graficar el método del codo
plot(k_values, wcss_values, type = "b", pch = 19, frame = FALSE,
     xlab = "Número de Clusters k",
     ylab = "WCSS (Suma de los Cuadrados Dentro del Grupo)",
     main = "Método del Codo para Optimización de Clusters")

## Comparar segmentos

asignacion$state <- rownames(asignacion)

# 4. Calculate average metrics by cluster for ranking
cluster_avg <- asignacion %>%
  group_by(cluster) %>%
  summarise(
    Murder = mean(Murder, na.rm = TRUE),
    Assault = mean(Assault, na.rm = TRUE),
    Rape = mean(Rape, na.rm = TRUE)
  ) %>%
  mutate(
    Suma = Murder + Assault + Rape,
    Ranking = rank(-Suma, ties.method = "min")  # Higher crime gets lower ranking (1)
  )

# 5. Create security level labels based on ranking
cluster_labels <- cluster_avg %>%
  mutate(nivel_seguridad = case_when(
    Ranking == 1 ~ "Muy inseguro",
    Ranking == 2 ~ "Inseguro", 
    Ranking == 3 ~ "Seguro",
    Ranking == 4 ~ "Muy seguro",
    TRUE ~ "Desconocido"
  )) %>%
  select(cluster, Ranking, nivel_seguridad)

# 6. Join labels back to the data
asignacion <- left_join(asignacion, cluster_labels, by = "cluster")

# 7. Get US map
us_map <- ne_states(country = "United States of America", returnclass = "sf")

# 8. Create manual mapping between map state names and USArrests state names
state_mapping <- data.frame(
  map_name = c(
    "Alabama", "Alaska", "Arizona", "Arkansas", "California", 
    "Colorado", "Connecticut", "Delaware", "Florida", "Georgia", 
    "Hawaii", "Idaho", "Illinois", "Indiana", "Iowa", 
    "Kansas", "Kentucky", "Louisiana", "Maine", "Maryland", 
    "Massachusetts", "Michigan", "Minnesota", "Mississippi", "Missouri", 
    "Montana", "Nebraska", "Nevada", "New Hampshire", "New Jersey", 
    "New Mexico", "New York", "North Carolina", "North Dakota", "Ohio", 
    "Oklahoma", "Oregon", "Pennsylvania", "Rhode Island", "South Carolina", 
    "South Dakota", "Tennessee", "Texas", "Utah", "Vermont", 
    "Virginia", "Washington", "West Virginia", "Wisconsin", "Wyoming"
  ),
  data_name = state.name  # This matches the row names in USArrests
)

# 9. Add mapping column to the map data
us_map$data_name <- NA
for (i in 1:nrow(state_mapping)) {
  idx <- which(us_map$name == state_mapping$map_name[i])
  if (length(idx) > 0) {
    us_map$data_name[idx] <- state_mapping$data_name[i]
  }
}

# 10. Join map with data
us_clustered <- left_join(us_map, asignacion, by = c("data_name" = "state"))

# 11. Define colors for the security levels
nivel_colors <- c(
  "Muy inseguro" = "red", 
  "Inseguro" = "orange", 
  "Seguro" = "yellow", 
  "Muy seguro" = "darkgreen",
  "Desconocido" = "gray"
)

# 12. Print summary to check
print("Cluster summaries with rankings:")
## [1] "Cluster summaries with rankings:"
print(cluster_labels)
## # A tibble: 4 × 3
##   cluster Ranking nivel_seguridad
##     <int>   <int> <chr>          
## 1       1       1 Muy inseguro   
## 2       2       2 Inseguro       
## 3       3       4 Muy seguro     
## 4       4       3 Seguro
print("Distribution of security levels:")
## [1] "Distribution of security levels:"
print(table(asignacion$nivel_seguridad))
## 
##     Inseguro Muy inseguro   Muy seguro       Seguro 
##           12           19            7           12
# 13. Create the map
ggplot(data = us_clustered) +
  geom_sf(aes(fill = nivel_seguridad), color = "black", size = 0.2) +
  scale_fill_manual(values = nivel_colors, 
                    name = "Nivel de Seguridad",
                    na.value = "gray") +
  labs(title = "Mapa de Seguridad en EE.UU. (1973)",
       subtitle = "Clasificación basada en tasas de criminalidad",
       caption = "Fuente: USArrests") +
  theme_minimal()

** NOTA: La variable que queremos predecir debe tener formato de FACTOR - en este caso serían los clusters**

CARET

Partir los datos 80-20

set.seed(123)
renglones_entrenamiento <- createDataPartition(asignacion$cluster, p=0.8, list = FALSE)
entrenamiento <- asignacion[renglones_entrenamiento, ] 
prueba <- asignacion[-renglones_entrenamiento, ]
entrenamiento <- entrenamiento %>% select(-state)
prueba <- prueba %>% select(-state)

Modelo 1. SVM Linear

# Asegurar que la variable dependiente 'cluster' es un factor en entrenamiento y prueba
entrenamiento$cluster <- factor(entrenamiento$cluster)
prueba$cluster <- factor(prueba$cluster)


# Entrenar el modelo SVM
modelo1 <- train(cluster ~ ., data = entrenamiento,  
                 method = "svmLinear", 
                 preProcess = c("scale", "center"),
                 trControl = trainControl(method = "cv", number = 10),
                 tuneGrid = data.frame(C = 1)  
)

# Hacer predicciones
resultado_entrenamiento1 <- predict(modelo1, entrenamiento)
resultado_prueba1 <- predict(modelo1, prueba)

# Convertir las predicciones en factor con los mismos niveles que 'entrenamiento$cluster'
resultado_entrenamiento1 <- factor(resultado_entrenamiento1, levels = levels(entrenamiento$cluster))
resultado_prueba1 <- factor(resultado_prueba1, levels = levels(prueba$cluster))

# Matriz de Confusión del Entrenamiento
mcre1 <- confusionMatrix(resultado_entrenamiento1, entrenamiento$cluster)
print(mcre1)
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction  1  2  3  4
##          1 15  0  0  0
##          2  0 10  0  0
##          3  0  0  6  0
##          4  0  0  0 10
## 
## Overall Statistics
##                                     
##                Accuracy : 1         
##                  95% CI : (0.914, 1)
##     No Information Rate : 0.3659    
##     P-Value [Acc > NIR] : < 2.2e-16 
##                                     
##                   Kappa : 1         
##                                     
##  Mcnemar's Test P-Value : NA        
## 
## Statistics by Class:
## 
##                      Class: 1 Class: 2 Class: 3 Class: 4
## Sensitivity            1.0000   1.0000   1.0000   1.0000
## Specificity            1.0000   1.0000   1.0000   1.0000
## Pos Pred Value         1.0000   1.0000   1.0000   1.0000
## Neg Pred Value         1.0000   1.0000   1.0000   1.0000
## Prevalence             0.3659   0.2439   0.1463   0.2439
## Detection Rate         0.3659   0.2439   0.1463   0.2439
## Detection Prevalence   0.3659   0.2439   0.1463   0.2439
## Balanced Accuracy      1.0000   1.0000   1.0000   1.0000
# Matriz de Confusión del Resultado de la Prueba
mcrp1 <- confusionMatrix(resultado_prueba1, prueba$cluster)
print(mcrp1)
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction 1 2 3 4
##          1 4 0 0 0
##          2 0 2 0 0
##          3 0 0 1 0
##          4 0 0 0 2
## 
## Overall Statistics
##                                      
##                Accuracy : 1          
##                  95% CI : (0.6637, 1)
##     No Information Rate : 0.4444     
##     P-Value [Acc > NIR] : 0.0006766  
##                                      
##                   Kappa : 1          
##                                      
##  Mcnemar's Test P-Value : NA         
## 
## Statistics by Class:
## 
##                      Class: 1 Class: 2 Class: 3 Class: 4
## Sensitivity            1.0000   1.0000   1.0000   1.0000
## Specificity            1.0000   1.0000   1.0000   1.0000
## Pos Pred Value         1.0000   1.0000   1.0000   1.0000
## Neg Pred Value         1.0000   1.0000   1.0000   1.0000
## Prevalence             0.4444   0.2222   0.1111   0.2222
## Detection Rate         0.4444   0.2222   0.1111   0.2222
## Detection Prevalence   0.4444   0.2222   0.1111   0.2222
## Balanced Accuracy      1.0000   1.0000   1.0000   1.0000

Resumen de Resultados

resultados <- data.frame(
  "SVM Lineal" = c(mcre1$overall["Accuracy"], mcrp1$overall["Accuracy"])
)
rownames(resultados) <- c("Precision de Entrenamiento", "Precision de Prueba")
resultados
##                            SVM.Lineal
## Precision de Entrenamiento          1
## Precision de Prueba                 1
LS0tCnRpdGxlOiAiVVNBcnJlc3RzIgphdXRob3I6ICJNYXJpYSBEZWwgQm9zcXVlIgpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCm91dHB1dDogCiAgaHRtbF9kb2N1bWVudDogCiAgICB0b2M6IFRSVUUKICAgIHRvY19mbG9hdDogVFJVRQogICAgY29kZV9kb3dubG9hZDogVFJVRQogICAgdGhlbWU6IGpvdXJuYWwKLS0tCgohW10oL1VzZXJzL21hcmlhZGVsYm9zcXVlL0Rlc2t0b3AvVEVDL0NPTkNFTlRSQUNJT04vUi9nb3QtY2F1Z2h0LXBpbm5lZC1kb3duLmdpZikKCiMgPHNwYW4gc3R5bGU9ImNvbG9yOmJsdWUiPkNvbnRleHRvPC9zcGFuPgpMYSBiYXNlIGRlIGRhdG9zICpVU0FycmVzdHMqIGNvbnRpZW5lIGVzdGFkw61zdGljYXMgZW4gYXJyZXN0b3MgcG9yIGNhZGEgMTAwLDAwMCByZXNpZGVudGVzIHBvciBhZ3Jlc2nDs24sIGFzZXNpbmF0byB5IHZpb2xhY2nDs24gZW4gY2FkYSB1bm8gZGUgbG9zIDUwIGVzdGFkb3MgZGUgRUUuVVUuIGVuIDE5NzMuCgojIDxzcGFuIHN0eWxlPSJjb2xvcjpyZWQiPkluc3RhbGFyIHBhcXVldGVzIHkgbGxhbWFyIGxpYnJlcsOtYXM8L3NwYW4+CmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiNpbnN0YWxsLnBhY2thZ2VzKCJjbHVzdGVyIikgI1BhcmEgYWdydXBhbWllbnRvIGRlIGRhdG9zIApsaWJyYXJ5KGNsdXN0ZXIpCiNpbnN0YWxsLnBhY2thZ2VzKCJnZ3Bsb3QyIikgI0dyw6FmaWNhcwpsaWJyYXJ5KGdncGxvdDIpCiNpbnN0YWxsLnBhY2thZ2VzKCJmYWN0b2V4dHJhIikgI1Zpc3VhbGl6YXIgY2x1c3RlcnMKbGlicmFyeShmYWN0b2V4dHJhKQojaW5zdGFsbC5wYWNrYWdlcygiZGF0YS50YWJsZSIpICNDb25qdW50byBkZSBkYXRvcyBncmFuZGUgCmxpYnJhcnkoZGF0YS50YWJsZSkKI2luc3RhbGwucGFja2FnZXMoInRpZHl2ZXJzZSIpCmxpYnJhcnkodGlkeXZlcnNlKQojaW5zdGFsbC5wYWNrYWdlcygiY2FyZXQiKSAjIEFsZ29yaXRtb3MgZGUgYXByZW5kaXphamUgYXV0b23DoXRpY28KbGlicmFyeShjYXJldCkKI2luc3RhbGwucGFja2FnZXMoImRhdGFzZXRzIikgIyBQYXJhIHVzYXIgYmFzZSBkZSBkYXRvcyAiaXJpcyIKbGlicmFyeShkYXRhc2V0cykKI2luc3RhbGwucGFja2FnZXMoImdncGxvdDIiKSAjIEdyYWZpY2FzIGNvbiBtZWpvciBkaXNlw7FvCmxpYnJhcnkoZ2dwbG90MikKI2luc3RhbGwucGFja2FnZXMoImxhdHRpY2UiKSAjIENyZWFyIGdyw6FmaWNvcwpsaWJyYXJ5KGxhdHRpY2UpCiNpbnN0YWxsLnBhY2thZ2VzKCJEYXRhRXhwbG9yZXIiKSAjIEFuw6FsaXNpcyBEZXNjcmlwdGl2bwpsaWJyYXJ5KERhdGFFeHBsb3JlcikKI2luc3RhbGwucGFja2FnZXMoImtlcm5sYWIiKQpsaWJyYXJ5KGtlcm5sYWIpCiNpbnN0YWxsLnBhY2thZ2VzKCJyYW5kb21Gb3Jlc3QiKQpsaWJyYXJ5KHJhbmRvbUZvcmVzdCkKbGlicmFyeSh0aWJibGUpCiNpbnN0YWxsLnBhY2thZ2VzKCJzZiIpICNBbmFsaXNpcyBkZSBkYXRvcyBlc3BhY2lhbGVzCmxpYnJhcnkoc2YpCiNpbnN0YWxsLnBhY2thZ2VzKCJybmF0dXJhbGVhcnRoIikgI1Byb3BvcmNpb25hIGzDrW1pdGVzIGdlb2dyYWZpY29zCmxpYnJhcnkocm5hdHVyYWxlYXJ0aCkKI2luc3RhbGwucGFja2FnZXMoInJuYXR1cmFsZWFydGhkYXRhIikgI0RhdG9zIGRlIGdlb2dyYWZpYQpsaWJyYXJ5KHJuYXR1cmFsZWFydGhkYXRhKQojaW5zdGFsbC5wYWNrYWdlcygiZGV2dG9vbHMiKQpsaWJyYXJ5KGRldnRvb2xzKQpkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoInJvcGVuc2NpL3JuYXR1cmFsZWFydGhoaXJlcyIpICNNYXBhIGRlIE3DqXhpY28gcGFydGljdWxhcgpgYGAKCiMgPHNwYW4gc3R5bGU9ImNvbG9yOnJlZCI+SW1wb3J0YXIgYmFzZSBkZSBkYXRvczwvc3Bhbj4KYGBge3J9CmRmIDwtIFVTQXJyZXN0cwpgYGAKCiMgPHNwYW4gc3R5bGU9ImNvbG9yOiByZWQ7Ij5BbsOhbGlzaXMgZGVzY3JpcHRpdm88L3NwYW4+CmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHBhZ2VkLnByaW50PUZBTFNFfQpjcmVhdGVfcmVwb3J0KGRmKQpwbG90X21pc3NpbmcoZGYpCnBsb3RfaGlzdG9ncmFtKGRmKQpwbG90X2NvcnJlbGF0aW9uKGRmKQpgYGAKIyA8c3BhbiBzdHlsZT0iY29sb3I6IGJsdWU7Ij5DbHVzdGVyczwvc3Bhbj4KCiMjIDxzcGFuIHN0eWxlPSJjb2xvcjogcmVkOyI+RXNjYWxhciBsYSBiYXNlIGRlIGRhdG9zPC9zcGFuPgpgYGB7cn0KZGZfZXNjYWxhZG8gPC1zY2FsZShkZikKZGZfZXNjYWxhZG8gPC0gc3Vic2V0KGRmX2VzY2FsYWRvLHNlbGVjdCA9IC1VcmJhblBvcCkKZGZfZXNjYWxhZG8gPC1zY2FsZShkZl9lc2NhbGFkbykKYGBgCiMjIDxzcGFuIHN0eWxlPSJjb2xvcjogcmVkOyI+R2VuZXJhciBsb3Mgc2VnbWVudG9zPC9zcGFuPgpgYGB7cn0KZ3J1cG9zIDwtIDQgI0luaWNpbyBjb24gY3VhbHF1aWVyIHZhbG9yIHkgbHVlZ28gdmVyaWZpY28Kc2VnbWVudG9zIDwtIGttZWFucyhkZl9lc2NhbGFkbywgZ3J1cG9zKQpgYGAKCiMjIDxzcGFuIHN0eWxlPSJjb2xvcjogcmVkOyI+QXNpZ25hciBncnVwb3MgYSBsb3MgZGF0b3M8L3NwYW4+CmBgYHtyfQphc2lnbmFjaW9uIDwtIGNiaW5kKGRmLCBjbHVzdGVyPXNlZ21lbnRvcyRjbHVzdGVyKQpgYGAKCiMjIDxzcGFuIHN0eWxlPSJjb2xvcjogcmVkOyI+R3JhZmljYXIgbG9zIGNsdXN0ZXJzPC9zcGFuPgpgYGB7cn0KZnZpel9jbHVzdGVyKHNlZ21lbnRvcywgZGF0YSA9IGRmKQpgYGAKIyMgPHNwYW4gc3R5bGU9ImNvbG9yOiByZWQ7Ij5PcHRpbWl6YXIgbGEgY2FudGlkYWQgZGUgZ3J1cG9zPC9zcGFuPgpgYGB7cn0KI0xhIGNhbnRpZGFkIMOzcHRpbWEgZGUgZ3J1cG9zIGNvcnJlc3BvbmRlIGFsIHByaW1lciBwdW50byBtw6FzIGFsdG8gZGUgbGEgZ3LDoWZpY2EKc2V0LnNlZWQoMTIzKQojb3B0aW1pemFjaW9uIDwtIGNsdXNHYXAoZGZfZXNjYWxhZG8sIEZVTj1rbWVhbnMsIG5zdGFydD0xLCBLLm1heCA9MTApCiNwbG90KG9wdGltaXphY2lvbiwgeGxhYiA9ICJOw7ptZXJvIGRlIENsdXN0ZXJzIGsiKQoKIyBDcmVhciB1bmEgZnVuY2nDs24gcGFyYSBjYWxjdWxhciBsYSBzdW1hIGRlIGxvcyBjdWFkcmFkb3MgZGVudHJvIGRlbCBncnVwbyAoV0NTUykKd2NzcyA8LSBmdW5jdGlvbihrKSB7CiAga21lYW5zKGRmX2VzY2FsYWRvLCBjZW50ZXJzID0gaywgbnN0YXJ0ID0gMTApJHRvdC53aXRoaW5zcwp9CgojIEV2YWx1YXIgV0NTUyBwYXJhIGRpZmVyZW50ZXMgdmFsb3JlcyBkZSBrCmtfdmFsdWVzIDwtIDE6MTAKd2Nzc192YWx1ZXMgPC0gc2FwcGx5KGtfdmFsdWVzLCB3Y3NzKQoKIyBHcmFmaWNhciBlbCBtw6l0b2RvIGRlbCBjb2RvCnBsb3Qoa192YWx1ZXMsIHdjc3NfdmFsdWVzLCB0eXBlID0gImIiLCBwY2ggPSAxOSwgZnJhbWUgPSBGQUxTRSwKICAgICB4bGFiID0gIk7Dum1lcm8gZGUgQ2x1c3RlcnMgayIsCiAgICAgeWxhYiA9ICJXQ1NTIChTdW1hIGRlIGxvcyBDdWFkcmFkb3MgRGVudHJvIGRlbCBHcnVwbykiLAogICAgIG1haW4gPSAiTcOpdG9kbyBkZWwgQ29kbyBwYXJhIE9wdGltaXphY2nDs24gZGUgQ2x1c3RlcnMiKQoKYGBgCiMjIDxzcGFuIHN0eWxlPSJjb2xvcjogcmVkOyI+Q29tcGFyYXIgc2VnbWVudG9zPC9zcGFuPgpgYGB7cn0KYXNpZ25hY2lvbiRzdGF0ZSA8LSByb3duYW1lcyhhc2lnbmFjaW9uKQoKIyA0LiBDYWxjdWxhdGUgYXZlcmFnZSBtZXRyaWNzIGJ5IGNsdXN0ZXIgZm9yIHJhbmtpbmcKY2x1c3Rlcl9hdmcgPC0gYXNpZ25hY2lvbiAlPiUKICBncm91cF9ieShjbHVzdGVyKSAlPiUKICBzdW1tYXJpc2UoCiAgICBNdXJkZXIgPSBtZWFuKE11cmRlciwgbmEucm0gPSBUUlVFKSwKICAgIEFzc2F1bHQgPSBtZWFuKEFzc2F1bHQsIG5hLnJtID0gVFJVRSksCiAgICBSYXBlID0gbWVhbihSYXBlLCBuYS5ybSA9IFRSVUUpCiAgKSAlPiUKICBtdXRhdGUoCiAgICBTdW1hID0gTXVyZGVyICsgQXNzYXVsdCArIFJhcGUsCiAgICBSYW5raW5nID0gcmFuaygtU3VtYSwgdGllcy5tZXRob2QgPSAibWluIikgICMgSGlnaGVyIGNyaW1lIGdldHMgbG93ZXIgcmFua2luZyAoMSkKICApCgojIDUuIENyZWF0ZSBzZWN1cml0eSBsZXZlbCBsYWJlbHMgYmFzZWQgb24gcmFua2luZwpjbHVzdGVyX2xhYmVscyA8LSBjbHVzdGVyX2F2ZyAlPiUKICBtdXRhdGUobml2ZWxfc2VndXJpZGFkID0gY2FzZV93aGVuKAogICAgUmFua2luZyA9PSAxIH4gIk11eSBpbnNlZ3VybyIsCiAgICBSYW5raW5nID09IDIgfiAiSW5zZWd1cm8iLCAKICAgIFJhbmtpbmcgPT0gMyB+ICJTZWd1cm8iLAogICAgUmFua2luZyA9PSA0IH4gIk11eSBzZWd1cm8iLAogICAgVFJVRSB+ICJEZXNjb25vY2lkbyIKICApKSAlPiUKICBzZWxlY3QoY2x1c3RlciwgUmFua2luZywgbml2ZWxfc2VndXJpZGFkKQoKIyA2LiBKb2luIGxhYmVscyBiYWNrIHRvIHRoZSBkYXRhCmFzaWduYWNpb24gPC0gbGVmdF9qb2luKGFzaWduYWNpb24sIGNsdXN0ZXJfbGFiZWxzLCBieSA9ICJjbHVzdGVyIikKCiMgNy4gR2V0IFVTIG1hcAp1c19tYXAgPC0gbmVfc3RhdGVzKGNvdW50cnkgPSAiVW5pdGVkIFN0YXRlcyBvZiBBbWVyaWNhIiwgcmV0dXJuY2xhc3MgPSAic2YiKQoKIyA4LiBDcmVhdGUgbWFudWFsIG1hcHBpbmcgYmV0d2VlbiBtYXAgc3RhdGUgbmFtZXMgYW5kIFVTQXJyZXN0cyBzdGF0ZSBuYW1lcwpzdGF0ZV9tYXBwaW5nIDwtIGRhdGEuZnJhbWUoCiAgbWFwX25hbWUgPSBjKAogICAgIkFsYWJhbWEiLCAiQWxhc2thIiwgIkFyaXpvbmEiLCAiQXJrYW5zYXMiLCAiQ2FsaWZvcm5pYSIsIAogICAgIkNvbG9yYWRvIiwgIkNvbm5lY3RpY3V0IiwgIkRlbGF3YXJlIiwgIkZsb3JpZGEiLCAiR2VvcmdpYSIsIAogICAgIkhhd2FpaSIsICJJZGFobyIsICJJbGxpbm9pcyIsICJJbmRpYW5hIiwgIklvd2EiLCAKICAgICJLYW5zYXMiLCAiS2VudHVja3kiLCAiTG91aXNpYW5hIiwgIk1haW5lIiwgIk1hcnlsYW5kIiwgCiAgICAiTWFzc2FjaHVzZXR0cyIsICJNaWNoaWdhbiIsICJNaW5uZXNvdGEiLCAiTWlzc2lzc2lwcGkiLCAiTWlzc291cmkiLCAKICAgICJNb250YW5hIiwgIk5lYnJhc2thIiwgIk5ldmFkYSIsICJOZXcgSGFtcHNoaXJlIiwgIk5ldyBKZXJzZXkiLCAKICAgICJOZXcgTWV4aWNvIiwgIk5ldyBZb3JrIiwgIk5vcnRoIENhcm9saW5hIiwgIk5vcnRoIERha290YSIsICJPaGlvIiwgCiAgICAiT2tsYWhvbWEiLCAiT3JlZ29uIiwgIlBlbm5zeWx2YW5pYSIsICJSaG9kZSBJc2xhbmQiLCAiU291dGggQ2Fyb2xpbmEiLCAKICAgICJTb3V0aCBEYWtvdGEiLCAiVGVubmVzc2VlIiwgIlRleGFzIiwgIlV0YWgiLCAiVmVybW9udCIsIAogICAgIlZpcmdpbmlhIiwgIldhc2hpbmd0b24iLCAiV2VzdCBWaXJnaW5pYSIsICJXaXNjb25zaW4iLCAiV3lvbWluZyIKICApLAogIGRhdGFfbmFtZSA9IHN0YXRlLm5hbWUgICMgVGhpcyBtYXRjaGVzIHRoZSByb3cgbmFtZXMgaW4gVVNBcnJlc3RzCikKCiMgOS4gQWRkIG1hcHBpbmcgY29sdW1uIHRvIHRoZSBtYXAgZGF0YQp1c19tYXAkZGF0YV9uYW1lIDwtIE5BCmZvciAoaSBpbiAxOm5yb3coc3RhdGVfbWFwcGluZykpIHsKICBpZHggPC0gd2hpY2godXNfbWFwJG5hbWUgPT0gc3RhdGVfbWFwcGluZyRtYXBfbmFtZVtpXSkKICBpZiAobGVuZ3RoKGlkeCkgPiAwKSB7CiAgICB1c19tYXAkZGF0YV9uYW1lW2lkeF0gPC0gc3RhdGVfbWFwcGluZyRkYXRhX25hbWVbaV0KICB9Cn0KCiMgMTAuIEpvaW4gbWFwIHdpdGggZGF0YQp1c19jbHVzdGVyZWQgPC0gbGVmdF9qb2luKHVzX21hcCwgYXNpZ25hY2lvbiwgYnkgPSBjKCJkYXRhX25hbWUiID0gInN0YXRlIikpCgojIDExLiBEZWZpbmUgY29sb3JzIGZvciB0aGUgc2VjdXJpdHkgbGV2ZWxzCm5pdmVsX2NvbG9ycyA8LSBjKAogICJNdXkgaW5zZWd1cm8iID0gInJlZCIsIAogICJJbnNlZ3VybyIgPSAib3JhbmdlIiwgCiAgIlNlZ3VybyIgPSAieWVsbG93IiwgCiAgIk11eSBzZWd1cm8iID0gImRhcmtncmVlbiIsCiAgIkRlc2Nvbm9jaWRvIiA9ICJncmF5IgopCgojIDEyLiBQcmludCBzdW1tYXJ5IHRvIGNoZWNrCnByaW50KCJDbHVzdGVyIHN1bW1hcmllcyB3aXRoIHJhbmtpbmdzOiIpCnByaW50KGNsdXN0ZXJfbGFiZWxzKQoKcHJpbnQoIkRpc3RyaWJ1dGlvbiBvZiBzZWN1cml0eSBsZXZlbHM6IikKcHJpbnQodGFibGUoYXNpZ25hY2lvbiRuaXZlbF9zZWd1cmlkYWQpKQoKIyAxMy4gQ3JlYXRlIHRoZSBtYXAKZ2dwbG90KGRhdGEgPSB1c19jbHVzdGVyZWQpICsKICBnZW9tX3NmKGFlcyhmaWxsID0gbml2ZWxfc2VndXJpZGFkKSwgY29sb3IgPSAiYmxhY2siLCBzaXplID0gMC4yKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gbml2ZWxfY29sb3JzLCAKICAgICAgICAgICAgICAgICAgICBuYW1lID0gIk5pdmVsIGRlIFNlZ3VyaWRhZCIsCiAgICAgICAgICAgICAgICAgICAgbmEudmFsdWUgPSAiZ3JheSIpICsKICBsYWJzKHRpdGxlID0gIk1hcGEgZGUgU2VndXJpZGFkIGVuIEVFLlVVLiAoMTk3MykiLAogICAgICAgc3VidGl0bGUgPSAiQ2xhc2lmaWNhY2nDs24gYmFzYWRhIGVuIHRhc2FzIGRlIGNyaW1pbmFsaWRhZCIsCiAgICAgICBjYXB0aW9uID0gIkZ1ZW50ZTogVVNBcnJlc3RzIikgKwogIHRoZW1lX21pbmltYWwoKQpgYGAKCioqIE5PVEE6IExhIHZhcmlhYmxlIHF1ZSBxdWVyZW1vcyBwcmVkZWNpciBkZWJlIHRlbmVyIGZvcm1hdG8gZGUgRkFDVE9SIC0gZW4gZXN0ZSBjYXNvIHNlcsOtYW4gbG9zIGNsdXN0ZXJzKioKCiMgPHNwYW4gc3R5bGU9ImNvbG9yOiBibHVlOyI+Q0FSRVQ8L3NwYW4+CgojIyA8c3BhbiBzdHlsZT0iY29sb3I6IGJsdWU7Ij5QYXJ0aXIgbG9zIGRhdG9zIDgwLTIwPC9zcGFuPgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpzZXQuc2VlZCgxMjMpCnJlbmdsb25lc19lbnRyZW5hbWllbnRvIDwtIGNyZWF0ZURhdGFQYXJ0aXRpb24oYXNpZ25hY2lvbiRjbHVzdGVyLCBwPTAuOCwgbGlzdCA9IEZBTFNFKQplbnRyZW5hbWllbnRvIDwtIGFzaWduYWNpb25bcmVuZ2xvbmVzX2VudHJlbmFtaWVudG8sIF0gCnBydWViYSA8LSBhc2lnbmFjaW9uWy1yZW5nbG9uZXNfZW50cmVuYW1pZW50bywgXQpgYGAKCgpgYGB7cn0KZW50cmVuYW1pZW50byA8LSBlbnRyZW5hbWllbnRvICU+JSBzZWxlY3QoLXN0YXRlKQpwcnVlYmEgPC0gcHJ1ZWJhICU+JSBzZWxlY3QoLXN0YXRlKQpgYGAKCiMgPHNwYW4gc3R5bGU9ImNvbG9yOiByZWQ7Ij5Nb2RlbG8gMS4gU1ZNIExpbmVhcjwvc3Bhbj4KYGBge3J9CiMgQXNlZ3VyYXIgcXVlIGxhIHZhcmlhYmxlIGRlcGVuZGllbnRlICdjbHVzdGVyJyBlcyB1biBmYWN0b3IgZW4gZW50cmVuYW1pZW50byB5IHBydWViYQplbnRyZW5hbWllbnRvJGNsdXN0ZXIgPC0gZmFjdG9yKGVudHJlbmFtaWVudG8kY2x1c3RlcikKcHJ1ZWJhJGNsdXN0ZXIgPC0gZmFjdG9yKHBydWViYSRjbHVzdGVyKQoKCiMgRW50cmVuYXIgZWwgbW9kZWxvIFNWTQptb2RlbG8xIDwtIHRyYWluKGNsdXN0ZXIgfiAuLCBkYXRhID0gZW50cmVuYW1pZW50bywgIAogICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJzdm1MaW5lYXIiLCAKICAgICAgICAgICAgICAgICBwcmVQcm9jZXNzID0gYygic2NhbGUiLCAiY2VudGVyIiksCiAgICAgICAgICAgICAgICAgdHJDb250cm9sID0gdHJhaW5Db250cm9sKG1ldGhvZCA9ICJjdiIsIG51bWJlciA9IDEwKSwKICAgICAgICAgICAgICAgICB0dW5lR3JpZCA9IGRhdGEuZnJhbWUoQyA9IDEpICAKKQoKIyBIYWNlciBwcmVkaWNjaW9uZXMKcmVzdWx0YWRvX2VudHJlbmFtaWVudG8xIDwtIHByZWRpY3QobW9kZWxvMSwgZW50cmVuYW1pZW50bykKcmVzdWx0YWRvX3BydWViYTEgPC0gcHJlZGljdChtb2RlbG8xLCBwcnVlYmEpCgojIENvbnZlcnRpciBsYXMgcHJlZGljY2lvbmVzIGVuIGZhY3RvciBjb24gbG9zIG1pc21vcyBuaXZlbGVzIHF1ZSAnZW50cmVuYW1pZW50byRjbHVzdGVyJwpyZXN1bHRhZG9fZW50cmVuYW1pZW50bzEgPC0gZmFjdG9yKHJlc3VsdGFkb19lbnRyZW5hbWllbnRvMSwgbGV2ZWxzID0gbGV2ZWxzKGVudHJlbmFtaWVudG8kY2x1c3RlcikpCnJlc3VsdGFkb19wcnVlYmExIDwtIGZhY3RvcihyZXN1bHRhZG9fcHJ1ZWJhMSwgbGV2ZWxzID0gbGV2ZWxzKHBydWViYSRjbHVzdGVyKSkKCiMgTWF0cml6IGRlIENvbmZ1c2nDs24gZGVsIEVudHJlbmFtaWVudG8KbWNyZTEgPC0gY29uZnVzaW9uTWF0cml4KHJlc3VsdGFkb19lbnRyZW5hbWllbnRvMSwgZW50cmVuYW1pZW50byRjbHVzdGVyKQpwcmludChtY3JlMSkKCiMgTWF0cml6IGRlIENvbmZ1c2nDs24gZGVsIFJlc3VsdGFkbyBkZSBsYSBQcnVlYmEKbWNycDEgPC0gY29uZnVzaW9uTWF0cml4KHJlc3VsdGFkb19wcnVlYmExLCBwcnVlYmEkY2x1c3RlcikKcHJpbnQobWNycDEpCmBgYAoKIyA8c3BhbiBzdHlsZT0iY29sb3I6IGJsdWU7Ij5SZXN1bWVuIGRlIFJlc3VsdGFkb3M8L3NwYW4+CmBgYHtyfQpyZXN1bHRhZG9zIDwtIGRhdGEuZnJhbWUoCiAgIlNWTSBMaW5lYWwiID0gYyhtY3JlMSRvdmVyYWxsWyJBY2N1cmFjeSJdLCBtY3JwMSRvdmVyYWxsWyJBY2N1cmFjeSJdKQopCnJvd25hbWVzKHJlc3VsdGFkb3MpIDwtIGMoIlByZWNpc2lvbiBkZSBFbnRyZW5hbWllbnRvIiwgIlByZWNpc2lvbiBkZSBQcnVlYmEiKQpyZXN1bHRhZG9zCmBgYAoK