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")
library(cluster)
#install.packages("ggplot2")
library(ggplot2)
#install.packages("data.table")
library(data.table)
#install.packages("factoextra")
library(factoextra)
## Welcome! Want to learn more? See two factoextra-related books at https://goo.gl/ve3WBa
#install.packages("maps")

library(maps)
## 
## Adjuntando el paquete: 'maps'
## The following object is masked from 'package:cluster':
## 
##     votes.repub
#install.packages("tibble")

library(tibble)
#install.packages("dplyr")

library(dplyr)
## 
## Adjuntando el paquete: 'dplyr'
## The following objects are masked from 'package:data.table':
## 
##     between, first, last
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union

Importar base de datos

datos <- USArrests

# Removiendo la variable "UrbanPop" para la segmentación
datos_seguridad <- datos %>% select(-UrbanPop)

Escalar las variables

# Convertir a matriz y escalar valores
datos_escalada <- scale(as.matrix(datos_seguridad))

# Verificar si hay valores NA y eliminarlos
datos_escalada <- na.omit(datos_escalada)

Optimizar la cantidad de grupos

set.seed(123)

# Corrección: Usar datos_escalada en lugar de df
optimizacion <- clusGap(datos_escalada, FUN = kmeans, nstart = 1, K.max = 10)

# Graficar el resultado
plot(optimizacion, xlab="Número de clusters k")

Cantidad de grupos

grupos <- 3

Generar los segmentos

set.seed(123)
segmentos <- kmeans(datos_escalada, centers = grupos, nstart = 25)

# Mostrar información del clustering
print(segmentos)
## K-means clustering with 3 clusters of sizes 19, 14, 17
## 
## Cluster means:
##       Murder   Assault       Rape
## 1  1.0431796  1.062614  0.8523875
## 2 -1.0812577 -1.077921 -1.0070054
## 3 -0.2754591 -0.299928 -0.1233698
## 
## Clustering vector:
##        Alabama         Alaska        Arizona       Arkansas     California 
##              1              1              1              3              1 
##       Colorado    Connecticut       Delaware        Florida        Georgia 
##              1              2              3              1              1 
##         Hawaii          Idaho       Illinois        Indiana           Iowa 
##              2              2              1              3              2 
##         Kansas       Kentucky      Louisiana          Maine       Maryland 
##              3              3              1              2              1 
##  Massachusetts       Michigan      Minnesota    Mississippi       Missouri 
##              3              1              2              1              3 
##        Montana       Nebraska         Nevada  New Hampshire     New Jersey 
##              3              2              1              2              3 
##     New Mexico       New York North Carolina   North Dakota           Ohio 
##              1              1              1              2              3 
##       Oklahoma         Oregon   Pennsylvania   Rhode Island South Carolina 
##              3              3              3              2              1 
##   South Dakota      Tennessee          Texas           Utah        Vermont 
##              2              1              1              3              2 
##       Virginia     Washington  West Virginia      Wisconsin        Wyoming 
##              3              3              2              2              3 
## 
## Within cluster sum of squares by cluster:
## [1] 26.305392  5.645542  9.205038
##  (between_SS / total_SS =  72.0 %)
## 
## Available components:
## 
## [1] "cluster"      "centers"      "totss"        "withinss"     "tot.withinss"
## [6] "betweenss"    "size"         "iter"         "ifault"

Asignar el grupo al que pertenece cada observación

asignacion <- data.frame(cluster = segmentos$cluster, datos)
head(asignacion)
##            cluster Murder Assault UrbanPop Rape
## Alabama          1   13.2     236       58 21.2
## Alaska           1   10.0     263       48 44.5
## Arizona          1    8.1     294       80 31.0
## Arkansas         3    8.8     190       50 19.5
## California       1    9.0     276       91 40.6
## Colorado         1    7.9     204       78 38.7

Graficar los clusters

fviz_cluster(segmentos, data = datos_escalada)

Comparar segmentos

# Calcular los promedios de cada cluster
promedio <- asignacion %>%
  group_by(cluster) %>%
  summarise(across(c(Murder, Assault, Rape), mean))

# Mostrar resultados
print(promedio)
## # A tibble: 3 × 4
##   cluster Murder Assault  Rape
##     <int>  <dbl>   <dbl> <dbl>
## 1       1  12.3    259.   29.2
## 2       2   3.08    80.9  11.8
## 3       3   6.59   146.   20.1

Colorear el mapa

USArrests <- rownames_to_column(asignacion, "State")
head(USArrests)
##        State cluster Murder Assault UrbanPop Rape
## 1    Alabama       1   13.2     236       58 21.2
## 2     Alaska       1   10.0     263       48 44.5
## 3    Arizona       1    8.1     294       80 31.0
## 4   Arkansas       3    8.8     190       50 19.5
## 5 California       1    9.0     276       91 40.6
## 6   Colorado       1    7.9     204       78 38.7
cluster1 <- filter(USArrests, cluster == 2)
cluster2 <- filter(USArrests, cluster == 1)
cluster3 <- filter(USArrests, cluster == 3)

map(database = "state")
map(database = "state", regions = cluster1$State, col="#2b83ba", fill=TRUE, add = TRUE)
map(database = "state", regions = cluster2$State, col="#d7191c", fill=TRUE, add = TRUE)
map(database = "state", regions = cluster3$State, col="#abdda4", fill=TRUE, add = TRUE)
legend("topright", 
       legend = c("Muy Seguro", "Seguro", "Inseguro"), 
       fill = c("#2b83ba", "#abdda4", "#d7191c"),
       x = "bottomright", y = 1
)

table(segmentos$cluster)
## 
##  1  2  3 
## 19 14 17
asignacion$Nombre_de_clusters <- as.factor(asignacion$cluster)

Partir la base de datos en 80-20

library(caret)
## Cargando paquete requerido: lattice
# Partir la base de datos en 80-20
set.seed(123)
renglones_entrenamiento <- createDataPartition(asignacion$Nombre_de_clusters, p=0.8, list=FALSE)

# Crear conjuntos de entrenamiento y prueba
entrenamiento <- asignacion[renglones_entrenamiento, ]
prueba <- asignacion[-renglones_entrenamiento, ]

# Verificar tamaños de los conjuntos
dim(entrenamiento)
## [1] 42  6
dim(prueba)
## [1] 8 6

Árbol de decisión

# Instalar y cargar los paquetes necesarios
if (!requireNamespace("caret", quietly = TRUE)) {
    install.packages("caret")
}
if (!requireNamespace("nnet", quietly = TRUE)) {
    install.packages("nnet")
}

library(caret)
library(nnet)

# Asegurar que la variable de clusters está como factor
entrenamiento$Nombre_de_clusters <- as.factor(entrenamiento$Nombre_de_clusters)
prueba$Nombre_de_clusters <- as.factor(prueba$Nombre_de_clusters)

# Entrenar modelo de red neuronal con validación cruzada
set.seed(123)
modelo <- train(Nombre_de_clusters ~ ., 
                data = entrenamiento,
                method = "nnet", 
                preProcess = c("scale", "center"),
                trControl = trainControl(method = "cv", number = 10),
                trace = FALSE) # Evita salida de consola innecesaria

# Hacer predicciones en entrenamiento y prueba
resultado_Entrenamiento <- predict(modelo, entrenamiento)
resultado_prueba <- predict(modelo, prueba)

# Matriz de Confusión del Resultado del Entrenamiento 
mcre <- confusionMatrix(resultado_Entrenamiento, entrenamiento$Nombre_de_clusters)
print(mcre)  # Mostrar matriz de confusión
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction  1  2  3
##          1 16  0  0
##          2  0 12  0
##          3  0  0 14
## 
## Overall Statistics
##                                      
##                Accuracy : 1          
##                  95% CI : (0.9159, 1)
##     No Information Rate : 0.381      
##     P-Value [Acc > NIR] : < 2.2e-16  
##                                      
##                   Kappa : 1          
##                                      
##  Mcnemar's Test P-Value : NA         
## 
## Statistics by Class:
## 
##                      Class: 1 Class: 2 Class: 3
## Sensitivity             1.000   1.0000   1.0000
## Specificity             1.000   1.0000   1.0000
## Pos Pred Value          1.000   1.0000   1.0000
## Neg Pred Value          1.000   1.0000   1.0000
## Prevalence              0.381   0.2857   0.3333
## Detection Rate          0.381   0.2857   0.3333
## Detection Prevalence    0.381   0.2857   0.3333
## Balanced Accuracy       1.000   1.0000   1.0000
# Matriz de Confusión del Resultado de la Prueba
mcrp <- confusionMatrix(resultado_prueba, prueba$Nombre_de_clusters)
print(mcrp)  # Mostrar matriz de confusión en prueba
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction 1 2 3
##          1 3 0 0
##          2 0 2 0
##          3 0 0 3
## 
## Overall Statistics
##                                      
##                Accuracy : 1          
##                  95% CI : (0.6306, 1)
##     No Information Rate : 0.375      
##     P-Value [Acc > NIR] : 0.0003911  
##                                      
##                   Kappa : 1          
##                                      
##  Mcnemar's Test P-Value : NA         
## 
## Statistics by Class:
## 
##                      Class: 1 Class: 2 Class: 3
## Sensitivity             1.000     1.00    1.000
## Specificity             1.000     1.00    1.000
## Pos Pred Value          1.000     1.00    1.000
## Neg Pred Value          1.000     1.00    1.000
## Prevalence              0.375     0.25    0.375
## Detection Rate          0.375     0.25    0.375
## Detection Prevalence    0.375     0.25    0.375
## Balanced Accuracy       1.000     1.00    1.000
LS0tDQp0aXRsZTogIlVTQXJyZXN0cyAiDQphdXRob3I6IEFsbWEgU2FudGlhZ28gX0EwMDgzNjYzDQpkYXRlOiAiMjAyNC0wMi0yMCINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICB0b2M6IFRSVUUNCiAgICB0b2NfZmxvYXQ6IFRSVUUNCiAgICBjb2RlX2Rvd25sb2FkOiBUUlVFDQogICAgdGhlbWU6IGpvdXJuYWwNCi0tLQ0KDQoNCg0KIVsgXShDOlxcVXNlcnNcXGFsbWFpXFxEb3dubG9hZHNcXGd1YXJkaWFzLWRlLXNlZ3VyaWRhZC1mdW5jaW9uZXMud2VicCkNCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpDQpgYGANCg0KDQoNCiMgPHNwYW4gc3R5bGU9ImNvbG9yOnJlZDsiPioqQ29udGV4dG8qKjwvc3Bhbj4NCg0KTGEgYmFzZSBkZSBkYXRvcyAiKlVTQXJyZXN0cyoiIGNvbnRpZW5lIGVzdGFkw61zdGljYXMgZW4gYXJyZXN0b3MgcG9yIGNhZGEgMTAwLDAwMCByZXNpZGVudGVzIHBvciBhZ3Jlc2nDs24sIGFzZXNpbmF0byB5IHZpb2xhY2nDs24gZW4gY2FkYSB1bm8gZGUgbG9zIDUwIGVzdGFkb3MgZGUgRUUuVVUuIGVuIDE5NzMuDQoNCiMgPHNwYW4gc3R5bGU9ImNvbG9yOnJlZDsiPioqSW5zdGFsYXIgcGFxdWV0ZXMgeSBsbGFtYXIgbGlicmVyw61hcyoqPC9zcGFuPg0KDQpgYGB7ciB9DQojaW5zdGFsbC5wYWNrYWdlcygiY2x1c3RlciIpDQpsaWJyYXJ5KGNsdXN0ZXIpDQojaW5zdGFsbC5wYWNrYWdlcygiZ2dwbG90MiIpDQpsaWJyYXJ5KGdncGxvdDIpDQojaW5zdGFsbC5wYWNrYWdlcygiZGF0YS50YWJsZSIpDQpsaWJyYXJ5KGRhdGEudGFibGUpDQojaW5zdGFsbC5wYWNrYWdlcygiZmFjdG9leHRyYSIpDQpsaWJyYXJ5KGZhY3RvZXh0cmEpDQojaW5zdGFsbC5wYWNrYWdlcygibWFwcyIpDQoNCmxpYnJhcnkobWFwcykNCiNpbnN0YWxsLnBhY2thZ2VzKCJ0aWJibGUiKQ0KDQpsaWJyYXJ5KHRpYmJsZSkNCiNpbnN0YWxsLnBhY2thZ2VzKCJkcGx5ciIpDQoNCmxpYnJhcnkoZHBseXIpDQpgYGANCg0KIyA8c3BhbiBzdHlsZT0iY29sb3I6cmVkOyI+KipJbXBvcnRhciBiYXNlIGRlIGRhdG9zKio8L3NwYW4+DQoNCmBgYHtyIH0NCmRhdG9zIDwtIFVTQXJyZXN0cw0KDQojIFJlbW92aWVuZG8gbGEgdmFyaWFibGUgIlVyYmFuUG9wIiBwYXJhIGxhIHNlZ21lbnRhY2nDs24NCmRhdG9zX3NlZ3VyaWRhZCA8LSBkYXRvcyAlPiUgc2VsZWN0KC1VcmJhblBvcCkNCg0KDQpgYGANCg0KIyA8c3BhbiBzdHlsZT0iY29sb3I6cmVkOyI+KipFc2NhbGFyIGxhcyB2YXJpYWJsZXMqKjwvc3Bhbj4NCmBgYHtyIH0NCiMgQ29udmVydGlyIGEgbWF0cml6IHkgZXNjYWxhciB2YWxvcmVzDQpkYXRvc19lc2NhbGFkYSA8LSBzY2FsZShhcy5tYXRyaXgoZGF0b3Nfc2VndXJpZGFkKSkNCg0KIyBWZXJpZmljYXIgc2kgaGF5IHZhbG9yZXMgTkEgeSBlbGltaW5hcmxvcw0KZGF0b3NfZXNjYWxhZGEgPC0gbmEub21pdChkYXRvc19lc2NhbGFkYSkNCmBgYA0KDQojIDxzcGFuIHN0eWxlPSJjb2xvcjpyZWQ7Ij4qKk9wdGltaXphciBsYSBjYW50aWRhZCBkZSBncnVwb3MqKjwvc3Bhbj4NCg0KDQoNCg0KYGBge3IgfQ0Kc2V0LnNlZWQoMTIzKQ0KDQojIENvcnJlY2Npw7NuOiBVc2FyIGRhdG9zX2VzY2FsYWRhIGVuIGx1Z2FyIGRlIGRmDQpvcHRpbWl6YWNpb24gPC0gY2x1c0dhcChkYXRvc19lc2NhbGFkYSwgRlVOID0ga21lYW5zLCBuc3RhcnQgPSAxLCBLLm1heCA9IDEwKQ0KDQojIEdyYWZpY2FyIGVsIHJlc3VsdGFkbw0KcGxvdChvcHRpbWl6YWNpb24sIHhsYWI9Ik7Dum1lcm8gZGUgY2x1c3RlcnMgayIpDQoNCmBgYA0KDQojIDxzcGFuIHN0eWxlPSJjb2xvcjpyZWQ7Ij4qKkNhbnRpZGFkIGRlIGdydXBvcyoqPC9zcGFuPg0KDQoNCmBgYHtyIH0NCmdydXBvcyA8LSAzDQpgYGANCg0KIyA8c3BhbiBzdHlsZT0iY29sb3I6cmVkOyI+KipHZW5lcmFyIGxvcyBzZWdtZW50b3MqKjwvc3Bhbj4NCg0KYGBge3IgfQ0Kc2V0LnNlZWQoMTIzKQ0Kc2VnbWVudG9zIDwtIGttZWFucyhkYXRvc19lc2NhbGFkYSwgY2VudGVycyA9IGdydXBvcywgbnN0YXJ0ID0gMjUpDQoNCiMgTW9zdHJhciBpbmZvcm1hY2nDs24gZGVsIGNsdXN0ZXJpbmcNCnByaW50KHNlZ21lbnRvcykNCg0KYGBgDQoNCiMgPHNwYW4gc3R5bGU9ImNvbG9yOnJlZDsiPioqQXNpZ25hciBlbCBncnVwbyBhbCBxdWUgcGVydGVuZWNlIGNhZGEgb2JzZXJ2YWNpw7NuKio8L3NwYW4+DQoNCg0KYGBge3IgfQ0KYXNpZ25hY2lvbiA8LSBkYXRhLmZyYW1lKGNsdXN0ZXIgPSBzZWdtZW50b3MkY2x1c3RlciwgZGF0b3MpDQpoZWFkKGFzaWduYWNpb24pDQoNCmBgYA0KDQojIDxzcGFuIHN0eWxlPSJjb2xvcjpyZWQ7Ij4qKkdyYWZpY2FyIGxvcyBjbHVzdGVycyoqPC9zcGFuPg0KDQoNCmBgYHtyIH0NCmZ2aXpfY2x1c3RlcihzZWdtZW50b3MsIGRhdGEgPSBkYXRvc19lc2NhbGFkYSkNCmBgYA0KDQojIDxzcGFuIHN0eWxlPSJjb2xvcjpyZWQ7Ij4qKkNvbXBhcmFyIHNlZ21lbnRvcyoqPC9zcGFuPg0KDQpgYGB7cn0NCiMgQ2FsY3VsYXIgbG9zIHByb21lZGlvcyBkZSBjYWRhIGNsdXN0ZXINCnByb21lZGlvIDwtIGFzaWduYWNpb24gJT4lDQogIGdyb3VwX2J5KGNsdXN0ZXIpICU+JQ0KICBzdW1tYXJpc2UoYWNyb3NzKGMoTXVyZGVyLCBBc3NhdWx0LCBSYXBlKSwgbWVhbikpDQoNCiMgTW9zdHJhciByZXN1bHRhZG9zDQpwcmludChwcm9tZWRpbykNCg0KYGBgDQoNCg0KDQojIDxzcGFuIHN0eWxlPSJjb2xvcjpyZWQ7Ij4qKkNvbG9yZWFyIGVsIG1hcGEqKjwvc3Bhbj4NCg0KYGBge3IgfQ0KVVNBcnJlc3RzIDwtIHJvd25hbWVzX3RvX2NvbHVtbihhc2lnbmFjaW9uLCAiU3RhdGUiKQ0KaGVhZChVU0FycmVzdHMpDQpjbHVzdGVyMSA8LSBmaWx0ZXIoVVNBcnJlc3RzLCBjbHVzdGVyID09IDIpDQpjbHVzdGVyMiA8LSBmaWx0ZXIoVVNBcnJlc3RzLCBjbHVzdGVyID09IDEpDQpjbHVzdGVyMyA8LSBmaWx0ZXIoVVNBcnJlc3RzLCBjbHVzdGVyID09IDMpDQoNCm1hcChkYXRhYmFzZSA9ICJzdGF0ZSIpDQptYXAoZGF0YWJhc2UgPSAic3RhdGUiLCByZWdpb25zID0gY2x1c3RlcjEkU3RhdGUsIGNvbD0iIzJiODNiYSIsIGZpbGw9VFJVRSwgYWRkID0gVFJVRSkNCm1hcChkYXRhYmFzZSA9ICJzdGF0ZSIsIHJlZ2lvbnMgPSBjbHVzdGVyMiRTdGF0ZSwgY29sPSIjZDcxOTFjIiwgZmlsbD1UUlVFLCBhZGQgPSBUUlVFKQ0KbWFwKGRhdGFiYXNlID0gInN0YXRlIiwgcmVnaW9ucyA9IGNsdXN0ZXIzJFN0YXRlLCBjb2w9IiNhYmRkYTQiLCBmaWxsPVRSVUUsIGFkZCA9IFRSVUUpDQpsZWdlbmQoInRvcHJpZ2h0IiwgDQogICAgICAgbGVnZW5kID0gYygiTXV5IFNlZ3VybyIsICJTZWd1cm8iLCAiSW5zZWd1cm8iKSwgDQogICAgICAgZmlsbCA9IGMoIiMyYjgzYmEiLCAiI2FiZGRhNCIsICIjZDcxOTFjIiksDQogICAgICAgeCA9ICJib3R0b21yaWdodCIsIHkgPSAxDQopDQoNCg0KYGBgDQoNCmBgYHtyfQ0KdGFibGUoc2VnbWVudG9zJGNsdXN0ZXIpDQphc2lnbmFjaW9uJE5vbWJyZV9kZV9jbHVzdGVycyA8LSBhcy5mYWN0b3IoYXNpZ25hY2lvbiRjbHVzdGVyKQ0KDQpgYGANCg0KIyA8c3BhbiBzdHlsZT0iY29sb3I6cmVkOyI+KipQYXJ0aXIgbGEgYmFzZSBkZSBkYXRvcyBlbiA4MC0yMCoqPC9zcGFuPg0KDQpgYGB7cn0NCmxpYnJhcnkoY2FyZXQpDQoNCiMgUGFydGlyIGxhIGJhc2UgZGUgZGF0b3MgZW4gODAtMjANCnNldC5zZWVkKDEyMykNCnJlbmdsb25lc19lbnRyZW5hbWllbnRvIDwtIGNyZWF0ZURhdGFQYXJ0aXRpb24oYXNpZ25hY2lvbiROb21icmVfZGVfY2x1c3RlcnMsIHA9MC44LCBsaXN0PUZBTFNFKQ0KDQojIENyZWFyIGNvbmp1bnRvcyBkZSBlbnRyZW5hbWllbnRvIHkgcHJ1ZWJhDQplbnRyZW5hbWllbnRvIDwtIGFzaWduYWNpb25bcmVuZ2xvbmVzX2VudHJlbmFtaWVudG8sIF0NCnBydWViYSA8LSBhc2lnbmFjaW9uWy1yZW5nbG9uZXNfZW50cmVuYW1pZW50bywgXQ0KDQojIFZlcmlmaWNhciB0YW1hw7FvcyBkZSBsb3MgY29uanVudG9zDQpkaW0oZW50cmVuYW1pZW50bykNCmRpbShwcnVlYmEpDQoNCmBgYA0KDQojIDxzcGFuIHN0eWxlPSJjb2xvcjpyZWQ7Ij4qKsOBcmJvbCBkZSBkZWNpc2nDs24qKjwvc3Bhbj4NCmBgYHtyfQ0KIyBJbnN0YWxhciB5IGNhcmdhciBsb3MgcGFxdWV0ZXMgbmVjZXNhcmlvcw0KaWYgKCFyZXF1aXJlTmFtZXNwYWNlKCJjYXJldCIsIHF1aWV0bHkgPSBUUlVFKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoImNhcmV0IikNCn0NCmlmICghcmVxdWlyZU5hbWVzcGFjZSgibm5ldCIsIHF1aWV0bHkgPSBUUlVFKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoIm5uZXQiKQ0KfQ0KDQpsaWJyYXJ5KGNhcmV0KQ0KbGlicmFyeShubmV0KQ0KDQojIEFzZWd1cmFyIHF1ZSBsYSB2YXJpYWJsZSBkZSBjbHVzdGVycyBlc3TDoSBjb21vIGZhY3Rvcg0KZW50cmVuYW1pZW50byROb21icmVfZGVfY2x1c3RlcnMgPC0gYXMuZmFjdG9yKGVudHJlbmFtaWVudG8kTm9tYnJlX2RlX2NsdXN0ZXJzKQ0KcHJ1ZWJhJE5vbWJyZV9kZV9jbHVzdGVycyA8LSBhcy5mYWN0b3IocHJ1ZWJhJE5vbWJyZV9kZV9jbHVzdGVycykNCg0KIyBFbnRyZW5hciBtb2RlbG8gZGUgcmVkIG5ldXJvbmFsIGNvbiB2YWxpZGFjacOzbiBjcnV6YWRhDQpzZXQuc2VlZCgxMjMpDQptb2RlbG8gPC0gdHJhaW4oTm9tYnJlX2RlX2NsdXN0ZXJzIH4gLiwgDQogICAgICAgICAgICAgICAgZGF0YSA9IGVudHJlbmFtaWVudG8sDQogICAgICAgICAgICAgICAgbWV0aG9kID0gIm5uZXQiLCANCiAgICAgICAgICAgICAgICBwcmVQcm9jZXNzID0gYygic2NhbGUiLCAiY2VudGVyIiksDQogICAgICAgICAgICAgICAgdHJDb250cm9sID0gdHJhaW5Db250cm9sKG1ldGhvZCA9ICJjdiIsIG51bWJlciA9IDEwKSwNCiAgICAgICAgICAgICAgICB0cmFjZSA9IEZBTFNFKSAjIEV2aXRhIHNhbGlkYSBkZSBjb25zb2xhIGlubmVjZXNhcmlhDQoNCiMgSGFjZXIgcHJlZGljY2lvbmVzIGVuIGVudHJlbmFtaWVudG8geSBwcnVlYmENCnJlc3VsdGFkb19FbnRyZW5hbWllbnRvIDwtIHByZWRpY3QobW9kZWxvLCBlbnRyZW5hbWllbnRvKQ0KcmVzdWx0YWRvX3BydWViYSA8LSBwcmVkaWN0KG1vZGVsbywgcHJ1ZWJhKQ0KDQojIE1hdHJpeiBkZSBDb25mdXNpw7NuIGRlbCBSZXN1bHRhZG8gZGVsIEVudHJlbmFtaWVudG8gDQptY3JlIDwtIGNvbmZ1c2lvbk1hdHJpeChyZXN1bHRhZG9fRW50cmVuYW1pZW50bywgZW50cmVuYW1pZW50byROb21icmVfZGVfY2x1c3RlcnMpDQpwcmludChtY3JlKSAgIyBNb3N0cmFyIG1hdHJpeiBkZSBjb25mdXNpw7NuDQoNCiMgTWF0cml6IGRlIENvbmZ1c2nDs24gZGVsIFJlc3VsdGFkbyBkZSBsYSBQcnVlYmENCm1jcnAgPC0gY29uZnVzaW9uTWF0cml4KHJlc3VsdGFkb19wcnVlYmEsIHBydWViYSROb21icmVfZGVfY2x1c3RlcnMpDQpwcmludChtY3JwKSAgIyBNb3N0cmFyIG1hdHJpeiBkZSBjb25mdXNpw7NuIGVuIHBydWViYQ0KDQpgYGANCg0K