#Contexto La base de datos USArrests contiene estadisticas en arrestos por cada 100,000 residentes por agresion, asesinato y violacion en cada uno de los 50 estados de EE. UU. en 1973.

##Instalar paquetes y librerias

#install.packages("cluster") # Agrupamientos
library(cluster)
#install.packages("ggplot2") # Graficar
library(ggplot2)
#install.packages("factoextra") # Visualizar Clusters
library(factoextra)
#install.packages("data.table") # Manejo de datos grandes
library(data.table)
#install.packages("tidyverse") # Manipulación de datos
library(tidyverse)

##Importar base de datos

data("USArrests")

datos <- USArrests

##Escalar la base de datos

datos_escalados <- datos
datos_escalados <- subset(datos_escalados, select = -UrbanPop)  # Quitamos la columna de nombres
datos_escalados <- scale(datos_escalados)  # Normalizamos las variables

##Generar los segmentos

grupos <- 5 
segmentos <- kmeans(datos_escalados, grupos)

##Graficar y asignar grupo a los cluster

asignacion <- cbind(datos_escalados, cluster = segmentos$cluster)
fviz_cluster(segmentos, data = datos_escalados)

##Optimizar la cantidad de grupos

# Método del Codo para determinar el número óptimo de clusters
set.seed(123)
optimizacion <- clusGap(datos_escalados, FUN = kmeans, nstart = 1, K.max = 10)
plot(optimizacion, xlab = "Número de clusters")

##Comparar segmentos

# Asegurar que asignacion es un data frame
asignacion <- as.data.frame(cbind(datos, cluster = segmentos$cluster))

# Calcular los promedios por cluster
promedio <- aggregate(asignacion[, 1:4], by = list(Cluster = asignacion$cluster), FUN = mean)
print(promedio)
##   Cluster    Murder   Assault UrbanPop     Rape
## 1       1  2.791667  82.08333 56.16667 10.70833
## 2       2 10.100000 261.28571 74.57143 38.28571
## 3       3 13.633333 258.16667 64.66667 23.92500
## 4       4  6.887500 172.00000 69.00000 22.31250
## 5       5  6.045455 113.63636 68.45455 18.13636
table(asignacion$cluster)
## 
##  1  2  3  4  5 
## 12  7 12  8 11

##Inseguridad por promedio

# Crear una nueva columna con la suma de Murder, Assault y Rape en los promedios de los clusters
promedio$Total_Inseguridad <- promedio$Murder + promedio$Assault + promedio$Rape

# Ordenar de mayor a menor inseguridad
promedio_ordenado <- promedio[order(-promedio$Total_Inseguridad), ]

# Asignar categorías de inseguridad
promedio_ordenado$Clasificación <- factor(rank(-promedio_ordenado$Total_Inseguridad), 
                                          levels = 1:nrow(promedio_ordenado),
                                          labels = c("Más Inseguro", "Inseguro", "Moderado", "Seguro", "Más Seguro"))

print(promedio_ordenado)
##   Cluster    Murder   Assault UrbanPop     Rape Total_Inseguridad Clasificación
## 2       2 10.100000 261.28571 74.57143 38.28571         309.67143  Más Inseguro
## 3       3 13.633333 258.16667 64.66667 23.92500         295.72500      Inseguro
## 4       4  6.887500 172.00000 69.00000 22.31250         201.20000      Moderado
## 5       5  6.045455 113.63636 68.45455 18.13636         137.81818        Seguro
## 1       1  2.791667  82.08333 56.16667 10.70833          95.58333    Más Seguro

#Machine Learning

##Instalar paquetes y librerias

#install.packages("caret") # Algoritmos de aprendizaje automático
library(caret)
#install.packages("ggplot2")
library(ggplot2)
#install.packages("rpart") # Árboles de decisión
library(rpart)
#install.packages("rpart.plot") # Visualización de árboles
library(rpart.plot)
#install.packages("kernlab") # SVM
library(kernlab)
#install.packages("nnet") # Redes neuronales
library(nnet)
#install.packages("randomForest") # Bosques Aleatorios
library(randomForest)
library(caret)

##Importar base de datos

# Cambiar el nombre de la columna "cluster" a "clasificacion" (Por estetica)
colnames(asignacion)[colnames(asignacion) == "cluster"] <- "clasificacion"

# Reemplazar los valores numéricos por etiquetas de clasificación
asignacion$clasificacion <- factor(asignacion$clasificacion,
                                   levels = c(1, 2, 3, 4, 5),
                                   labels = c("Más inseguro", "Más seguro", "Seguro", "Moderado", "Inseguro"))

##Partir datos 80-20

set.seed(123)
renglones_entrenamiento <- createDataPartition(asignacion$clasificacion, p=0.8, list=FALSE)
entrenamiento <- asignacion[renglones_entrenamiento, ]
prueba <- asignacion[-renglones_entrenamiento, ]

##Modelo 1: SVM Lineal

modelo1 <- train(clasificacion ~ Murder + Assault + Rape, data=entrenamiento,
                 method = "svmLinear",
                 preProcess=c("scale", "center"),
                 trControl = trainControl(method = "cv", number=10),
                 tuneGrid = data.frame(C=1))

resultado_entrenamiento1 <- predict(modelo1, entrenamiento)
resultado_prueba1 <- predict(modelo1, prueba)

mcre1 <- confusionMatrix(resultado_entrenamiento1, entrenamiento$clasificacion)
mcrp1 <- confusionMatrix(resultado_prueba1, prueba$clasificacion)

##Modelo 2: SVM Radial

modelo2 <- train(clasificacion ~ Murder + Assault + Rape, data=entrenamiento,
                 method = "svmRadial",
                 preProcess=c("scale", "center"),
                 trControl = trainControl(method = "cv", number=10),
                 tuneGrid = data.frame(sigma=1, C=1))

resultado_entrenamiento2 <- predict(modelo2, entrenamiento)
resultado_prueba2 <- predict(modelo2, prueba)

mcre2 <- confusionMatrix(resultado_entrenamiento2, entrenamiento$clasificacion)
mcrp2 <- confusionMatrix(resultado_prueba2, prueba$clasificacion)

##Modelo 3: SVM Polinómico

modelo3 <- train(clasificacion ~ Murder + Assault + Rape, data=entrenamiento,
                 method = "svmPoly",
                 preProcess=c("scale", "center"),
                 trControl = trainControl(method = "cv", number=10),
                 tuneGrid = data.frame(degree=1, scale=1, C=1))

resultado_entrenamiento3 <- predict(modelo3, entrenamiento)
resultado_prueba3 <- predict(modelo3, prueba)

mcre3 <- confusionMatrix(resultado_entrenamiento3, entrenamiento$clasificacion)
mcrp3 <- confusionMatrix(resultado_prueba3, prueba$clasificacion)

##Modelo 4: Árbol de Decisión

modelo4 <- train(clasificacion ~ Murder + Assault + Rape, data=entrenamiento,
                 method = "rpart",
                 preProcess=c("scale", "center"),
                 trControl = trainControl(method = "cv", number=10),
                 tuneLength = 20,  # Aumenta la longitud para más profundidad
                 control = rpart.control(minsplit=2, cp=0.01, maxdepth=5) # Permite más divisiones
)

resultado_entrenamiento4 <- predict(modelo4, entrenamiento)
resultado_prueba4 <- predict(modelo4, prueba)

mcre4 <- confusionMatrix(resultado_entrenamiento4, entrenamiento$clasificacion)
mcrp4 <- confusionMatrix(resultado_prueba4, prueba$clasificacion)

rpart.plot(modelo4$finalModel, type=5, extra=104, fallen.leaves=TRUE, box.palette="Blues")

##Modelo 5: Redes Neuronales

modelo5 <- train(clasificacion ~ Murder + Assault + Rape, data=entrenamiento,
                 method = "nnet",
                 preProcess=c("scale", "center"),
                 trControl = trainControl(method = "cv", number=10),
                 trace=FALSE) 

resultado_entrenamiento5 <- predict(modelo5, entrenamiento)
resultado_prueba5 <- predict(modelo5, prueba)

mcre5 <- confusionMatrix(resultado_entrenamiento5, entrenamiento$clasificacion)
mcrp5 <- confusionMatrix(resultado_prueba5, prueba$clasificacion)

##Modelo 6: Random Forest

modelo6 <- train(clasificacion ~ Murder + Assault + Rape, data=entrenamiento,
                 method = "rf",
                 preProcess=c("scale", "center"),
                 trControl = trainControl(method = "cv", number=10),
                 tuneGrid = expand.grid(mtry = c(2, 4, 6))) 

resultado_entrenamiento6 <- predict(modelo6, entrenamiento)
resultado_prueba6 <- predict(modelo6, prueba)

mcre6 <- confusionMatrix(resultado_entrenamiento6, entrenamiento$clasificacion)
mcrp6 <- confusionMatrix(resultado_prueba6, prueba$clasificacion)

## Resumen de Resultados

resultados <- data.frame(
  "SVM Lineal" = c(mcre1$overall["Accuracy"], mcrp1$overall["Accuracy"]),
  "SVM Radial" = c(mcre2$overall["Accuracy"], mcrp2$overall["Accuracy"]),
  "SVM Polinómico" = c(mcre3$overall["Accuracy"], mcrp3$overall["Accuracy"]),
  "Árbol de decisión" = c(mcre4$overall["Accuracy"], mcrp4$overall["Accuracy"]),
  "Redes Neuronales" = c(mcre5$overall["Accuracy"], mcrp5$overall["Accuracy"]),
  "Bosques Aleatorios" = c(mcre6$overall["Accuracy"], mcrp6$overall["Accuracy"])
)

rownames(resultados) <- c("Precisión de Entrenamiento", "Precisión de Prueba")
resultados
##                            SVM.Lineal SVM.Radial SVM.Polinómico
## Precisión de Entrenamiento          1      1.000              1
## Precisión de Prueba                 1      0.875              1
##                            Árbol.de.decisión Redes.Neuronales
## Precisión de Entrenamiento         0.9761905            1.000
## Precisión de Prueba                0.5000000            0.875
##                            Bosques.Aleatorios
## Precisión de Entrenamiento               1.00
## Precisión de Prueba                      0.75
LS0tDQp0aXRsZTogIlVTQXJyZXN0cyINCm91dHB1dDogDQogIGh0bWxfZG9jdW1lbnQ6DQogICAgdG9jOiBUUlVFDQogICAgdG9jX2Zsb2F0OiBUUlVFDQogICAgY29kZV9kb3dubG9hZDogVFJVRQ0KICAgIHRoZW1lOiAiam91cm5hbCINCiAgICBoaWdobGlnaHQ6ICJrYXRlIg0KZGF0ZTogIjIwMjUtMDItMjEiDQotLS0NCiFbXShDOlxcVXNlcnNcXEx1aXMgQXJ0dXJvIFJlaW5vc29cXERvd25sb2Fkc1xcaW1hZ2VzICgxKS5qcGcpDQoNCg0KIzxzcGFuIHN0eWxlPSAiY29sb3I6IGJsdWU7Ij5Db250ZXh0bzwvc3Bhbj4NCkxhIGJhc2UgZGUgZGF0b3MgKipVU0FycmVzdHMqKiBjb250aWVuZSBlc3RhZGlzdGljYXMgZW4gYXJyZXN0b3MgcG9yIGNhZGEgMTAwLDAwMCByZXNpZGVudGVzIHBvciBhZ3Jlc2lvbiwgYXNlc2luYXRvIHkgdmlvbGFjaW9uIGVuIGNhZGEgdW5vIGRlIGxvcyA1MCBlc3RhZG9zIGRlIEVFLiBVVS4gZW4gMTk3My4NCg0KIyM8c3BhbiBzdHlsZT0gImNvbG9yOiBibHVlOyI+SW5zdGFsYXIgcGFxdWV0ZXMgeSBsaWJyZXJpYXM8L3NwYW4+DQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KI2luc3RhbGwucGFja2FnZXMoImNsdXN0ZXIiKSAjIEFncnVwYW1pZW50b3MNCmxpYnJhcnkoY2x1c3RlcikNCiNpbnN0YWxsLnBhY2thZ2VzKCJnZ3Bsb3QyIikgIyBHcmFmaWNhcg0KbGlicmFyeShnZ3Bsb3QyKQ0KI2luc3RhbGwucGFja2FnZXMoImZhY3RvZXh0cmEiKSAjIFZpc3VhbGl6YXIgQ2x1c3RlcnMNCmxpYnJhcnkoZmFjdG9leHRyYSkNCiNpbnN0YWxsLnBhY2thZ2VzKCJkYXRhLnRhYmxlIikgIyBNYW5lam8gZGUgZGF0b3MgZ3JhbmRlcw0KbGlicmFyeShkYXRhLnRhYmxlKQ0KI2luc3RhbGwucGFja2FnZXMoInRpZHl2ZXJzZSIpICMgTWFuaXB1bGFjacOzbiBkZSBkYXRvcw0KbGlicmFyeSh0aWR5dmVyc2UpDQpgYGANCg0KIyM8c3BhbiBzdHlsZT0gImNvbG9yOiBibHVlOyI+SW1wb3J0YXIgYmFzZSBkZSBkYXRvczwvc3Bhbj4NCmBgYHtyfQ0KZGF0YSgiVVNBcnJlc3RzIikNCg0KZGF0b3MgPC0gVVNBcnJlc3RzDQoNCmBgYA0KIyM8c3BhbiBzdHlsZT0gImNvbG9yOiBibHVlOyI+RXNjYWxhciBsYSBiYXNlIGRlIGRhdG9zPC9zcGFuPg0KDQpgYGB7cn0NCmRhdG9zX2VzY2FsYWRvcyA8LSBkYXRvcw0KZGF0b3NfZXNjYWxhZG9zIDwtIHN1YnNldChkYXRvc19lc2NhbGFkb3MsIHNlbGVjdCA9IC1VcmJhblBvcCkgICMgUXVpdGFtb3MgbGEgY29sdW1uYSBkZSBub21icmVzDQpkYXRvc19lc2NhbGFkb3MgPC0gc2NhbGUoZGF0b3NfZXNjYWxhZG9zKSAgIyBOb3JtYWxpemFtb3MgbGFzIHZhcmlhYmxlcw0KYGBgDQojIzxzcGFuIHN0eWxlPSAiY29sb3I6IGJsdWU7Ij5HZW5lcmFyIGxvcyBzZWdtZW50b3M8L3NwYW4+DQpgYGB7cn0NCmdydXBvcyA8LSA1IA0Kc2VnbWVudG9zIDwtIGttZWFucyhkYXRvc19lc2NhbGFkb3MsIGdydXBvcykNCg0KYGBgDQojIzxzcGFuIHN0eWxlPSAiY29sb3I6IGJsdWU7Ij5HcmFmaWNhciB5IGFzaWduYXIgZ3J1cG8gYSBsb3MgY2x1c3Rlcjwvc3Bhbj4NCmBgYHtyfQ0KYXNpZ25hY2lvbiA8LSBjYmluZChkYXRvc19lc2NhbGFkb3MsIGNsdXN0ZXIgPSBzZWdtZW50b3MkY2x1c3RlcikNCmZ2aXpfY2x1c3RlcihzZWdtZW50b3MsIGRhdGEgPSBkYXRvc19lc2NhbGFkb3MpDQoNCmBgYA0KIyM8c3BhbiBzdHlsZT0gImNvbG9yOiBibHVlOyI+T3B0aW1pemFyIGxhIGNhbnRpZGFkIGRlIGdydXBvczwvc3Bhbj4NCmBgYHtyfQ0KIyBNw6l0b2RvIGRlbCBDb2RvIHBhcmEgZGV0ZXJtaW5hciBlbCBuw7ptZXJvIMOzcHRpbW8gZGUgY2x1c3RlcnMNCnNldC5zZWVkKDEyMykNCm9wdGltaXphY2lvbiA8LSBjbHVzR2FwKGRhdG9zX2VzY2FsYWRvcywgRlVOID0ga21lYW5zLCBuc3RhcnQgPSAxLCBLLm1heCA9IDEwKQ0KcGxvdChvcHRpbWl6YWNpb24sIHhsYWIgPSAiTsO6bWVybyBkZSBjbHVzdGVycyIpDQoNCmBgYA0KIyM8c3BhbiBzdHlsZT0gImNvbG9yOiBibHVlOyI+Q29tcGFyYXIgc2VnbWVudG9zPC9zcGFuPg0KYGBge3J9DQojIEFzZWd1cmFyIHF1ZSBhc2lnbmFjaW9uIGVzIHVuIGRhdGEgZnJhbWUNCmFzaWduYWNpb24gPC0gYXMuZGF0YS5mcmFtZShjYmluZChkYXRvcywgY2x1c3RlciA9IHNlZ21lbnRvcyRjbHVzdGVyKSkNCg0KIyBDYWxjdWxhciBsb3MgcHJvbWVkaW9zIHBvciBjbHVzdGVyDQpwcm9tZWRpbyA8LSBhZ2dyZWdhdGUoYXNpZ25hY2lvblssIDE6NF0sIGJ5ID0gbGlzdChDbHVzdGVyID0gYXNpZ25hY2lvbiRjbHVzdGVyKSwgRlVOID0gbWVhbikNCnByaW50KHByb21lZGlvKQ0KdGFibGUoYXNpZ25hY2lvbiRjbHVzdGVyKQ0KDQoNCmBgYA0KDQojIzxzcGFuIHN0eWxlPSAiY29sb3I6IGJsdWU7Ij5JbnNlZ3VyaWRhZCBwb3IgcHJvbWVkaW88L3NwYW4+DQoNCmBgYHtyfQ0KIyBDcmVhciB1bmEgbnVldmEgY29sdW1uYSBjb24gbGEgc3VtYSBkZSBNdXJkZXIsIEFzc2F1bHQgeSBSYXBlIGVuIGxvcyBwcm9tZWRpb3MgZGUgbG9zIGNsdXN0ZXJzDQpwcm9tZWRpbyRUb3RhbF9JbnNlZ3VyaWRhZCA8LSBwcm9tZWRpbyRNdXJkZXIgKyBwcm9tZWRpbyRBc3NhdWx0ICsgcHJvbWVkaW8kUmFwZQ0KDQojIE9yZGVuYXIgZGUgbWF5b3IgYSBtZW5vciBpbnNlZ3VyaWRhZA0KcHJvbWVkaW9fb3JkZW5hZG8gPC0gcHJvbWVkaW9bb3JkZXIoLXByb21lZGlvJFRvdGFsX0luc2VndXJpZGFkKSwgXQ0KDQojIEFzaWduYXIgY2F0ZWdvcsOtYXMgZGUgaW5zZWd1cmlkYWQNCnByb21lZGlvX29yZGVuYWRvJENsYXNpZmljYWNpw7NuIDwtIGZhY3RvcihyYW5rKC1wcm9tZWRpb19vcmRlbmFkbyRUb3RhbF9JbnNlZ3VyaWRhZCksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gMTpucm93KHByb21lZGlvX29yZGVuYWRvKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIk3DoXMgSW5zZWd1cm8iLCAiSW5zZWd1cm8iLCAiTW9kZXJhZG8iLCAiU2VndXJvIiwgIk3DoXMgU2VndXJvIikpDQoNCnByaW50KHByb21lZGlvX29yZGVuYWRvKQ0KDQoNCmBgYA0KIzxzcGFuIHN0eWxlPSAiY29sb3I6IGJsdWU7Ij5NYWNoaW5lIExlYXJuaW5nPC9zcGFuPg0KDQojIzxzcGFuIHN0eWxlPSAiY29sb3I6IGJsdWU7Ij5JbnN0YWxhciBwYXF1ZXRlcyB5IGxpYnJlcmlhczwvc3Bhbj4NCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiNpbnN0YWxsLnBhY2thZ2VzKCJjYXJldCIpICMgQWxnb3JpdG1vcyBkZSBhcHJlbmRpemFqZSBhdXRvbcOhdGljbw0KbGlicmFyeShjYXJldCkNCiNpbnN0YWxsLnBhY2thZ2VzKCJnZ3Bsb3QyIikNCmxpYnJhcnkoZ2dwbG90MikNCiNpbnN0YWxsLnBhY2thZ2VzKCJycGFydCIpICMgw4FyYm9sZXMgZGUgZGVjaXNpw7NuDQpsaWJyYXJ5KHJwYXJ0KQ0KI2luc3RhbGwucGFja2FnZXMoInJwYXJ0LnBsb3QiKSAjIFZpc3VhbGl6YWNpw7NuIGRlIMOhcmJvbGVzDQpsaWJyYXJ5KHJwYXJ0LnBsb3QpDQojaW5zdGFsbC5wYWNrYWdlcygia2VybmxhYiIpICMgU1ZNDQpsaWJyYXJ5KGtlcm5sYWIpDQojaW5zdGFsbC5wYWNrYWdlcygibm5ldCIpICMgUmVkZXMgbmV1cm9uYWxlcw0KbGlicmFyeShubmV0KQ0KI2luc3RhbGwucGFja2FnZXMoInJhbmRvbUZvcmVzdCIpICMgQm9zcXVlcyBBbGVhdG9yaW9zDQpsaWJyYXJ5KHJhbmRvbUZvcmVzdCkNCmxpYnJhcnkoY2FyZXQpDQoNCmBgYA0KIyM8c3BhbiBzdHlsZT0gImNvbG9yOiBibHVlOyI+SW1wb3J0YXIgYmFzZSBkZSBkYXRvczwvc3Bhbj4NCmBgYHtyfQ0KDQojIENhbWJpYXIgZWwgbm9tYnJlIGRlIGxhIGNvbHVtbmEgImNsdXN0ZXIiIGEgImNsYXNpZmljYWNpb24iIChQb3IgZXN0ZXRpY2EpDQpjb2xuYW1lcyhhc2lnbmFjaW9uKVtjb2xuYW1lcyhhc2lnbmFjaW9uKSA9PSAiY2x1c3RlciJdIDwtICJjbGFzaWZpY2FjaW9uIg0KDQojIFJlZW1wbGF6YXIgbG9zIHZhbG9yZXMgbnVtw6lyaWNvcyBwb3IgZXRpcXVldGFzIGRlIGNsYXNpZmljYWNpw7NuDQphc2lnbmFjaW9uJGNsYXNpZmljYWNpb24gPC0gZmFjdG9yKGFzaWduYWNpb24kY2xhc2lmaWNhY2lvbiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygxLCAyLCAzLCA0LCA1KSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiTcOhcyBpbnNlZ3VybyIsICJNw6FzIHNlZ3VybyIsICJTZWd1cm8iLCAiTW9kZXJhZG8iLCAiSW5zZWd1cm8iKSkNCg0KDQoNCmBgYA0KIyM8c3BhbiBzdHlsZT0gImNvbG9yOiBibHVlOyI+UGFydGlyIGRhdG9zIDgwLTIwPC9zcGFuPg0KYGBge3J9DQpzZXQuc2VlZCgxMjMpDQpyZW5nbG9uZXNfZW50cmVuYW1pZW50byA8LSBjcmVhdGVEYXRhUGFydGl0aW9uKGFzaWduYWNpb24kY2xhc2lmaWNhY2lvbiwgcD0wLjgsIGxpc3Q9RkFMU0UpDQplbnRyZW5hbWllbnRvIDwtIGFzaWduYWNpb25bcmVuZ2xvbmVzX2VudHJlbmFtaWVudG8sIF0NCnBydWViYSA8LSBhc2lnbmFjaW9uWy1yZW5nbG9uZXNfZW50cmVuYW1pZW50bywgXQ0KDQoNCmBgYA0KIyM8c3BhbiBzdHlsZT0gImNvbG9yOiBibHVlOyI+TW9kZWxvIDE6IFNWTSBMaW5lYWw8L3NwYW4+DQpgYGB7cn0NCm1vZGVsbzEgPC0gdHJhaW4oY2xhc2lmaWNhY2lvbiB+IE11cmRlciArIEFzc2F1bHQgKyBSYXBlLCBkYXRhPWVudHJlbmFtaWVudG8sDQogICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJzdm1MaW5lYXIiLA0KICAgICAgICAgICAgICAgICBwcmVQcm9jZXNzPWMoInNjYWxlIiwgImNlbnRlciIpLA0KICAgICAgICAgICAgICAgICB0ckNvbnRyb2wgPSB0cmFpbkNvbnRyb2wobWV0aG9kID0gImN2IiwgbnVtYmVyPTEwKSwNCiAgICAgICAgICAgICAgICAgdHVuZUdyaWQgPSBkYXRhLmZyYW1lKEM9MSkpDQoNCnJlc3VsdGFkb19lbnRyZW5hbWllbnRvMSA8LSBwcmVkaWN0KG1vZGVsbzEsIGVudHJlbmFtaWVudG8pDQpyZXN1bHRhZG9fcHJ1ZWJhMSA8LSBwcmVkaWN0KG1vZGVsbzEsIHBydWViYSkNCg0KbWNyZTEgPC0gY29uZnVzaW9uTWF0cml4KHJlc3VsdGFkb19lbnRyZW5hbWllbnRvMSwgZW50cmVuYW1pZW50byRjbGFzaWZpY2FjaW9uKQ0KbWNycDEgPC0gY29uZnVzaW9uTWF0cml4KHJlc3VsdGFkb19wcnVlYmExLCBwcnVlYmEkY2xhc2lmaWNhY2lvbikNCg0KDQpgYGANCiMjPHNwYW4gc3R5bGU9ICJjb2xvcjogYmx1ZTsiPk1vZGVsbyAyOiBTVk0gUmFkaWFsPC9zcGFuPg0KYGBge3J9DQptb2RlbG8yIDwtIHRyYWluKGNsYXNpZmljYWNpb24gfiBNdXJkZXIgKyBBc3NhdWx0ICsgUmFwZSwgZGF0YT1lbnRyZW5hbWllbnRvLA0KICAgICAgICAgICAgICAgICBtZXRob2QgPSAic3ZtUmFkaWFsIiwNCiAgICAgICAgICAgICAgICAgcHJlUHJvY2Vzcz1jKCJzY2FsZSIsICJjZW50ZXIiKSwNCiAgICAgICAgICAgICAgICAgdHJDb250cm9sID0gdHJhaW5Db250cm9sKG1ldGhvZCA9ICJjdiIsIG51bWJlcj0xMCksDQogICAgICAgICAgICAgICAgIHR1bmVHcmlkID0gZGF0YS5mcmFtZShzaWdtYT0xLCBDPTEpKQ0KDQpyZXN1bHRhZG9fZW50cmVuYW1pZW50bzIgPC0gcHJlZGljdChtb2RlbG8yLCBlbnRyZW5hbWllbnRvKQ0KcmVzdWx0YWRvX3BydWViYTIgPC0gcHJlZGljdChtb2RlbG8yLCBwcnVlYmEpDQoNCm1jcmUyIDwtIGNvbmZ1c2lvbk1hdHJpeChyZXN1bHRhZG9fZW50cmVuYW1pZW50bzIsIGVudHJlbmFtaWVudG8kY2xhc2lmaWNhY2lvbikNCm1jcnAyIDwtIGNvbmZ1c2lvbk1hdHJpeChyZXN1bHRhZG9fcHJ1ZWJhMiwgcHJ1ZWJhJGNsYXNpZmljYWNpb24pDQoNCg0KYGBgDQojIzxzcGFuIHN0eWxlPSAiY29sb3I6IGJsdWU7Ij5Nb2RlbG8gMzogU1ZNIFBvbGluw7NtaWNvPC9zcGFuPg0KYGBge3J9DQptb2RlbG8zIDwtIHRyYWluKGNsYXNpZmljYWNpb24gfiBNdXJkZXIgKyBBc3NhdWx0ICsgUmFwZSwgZGF0YT1lbnRyZW5hbWllbnRvLA0KICAgICAgICAgICAgICAgICBtZXRob2QgPSAic3ZtUG9seSIsDQogICAgICAgICAgICAgICAgIHByZVByb2Nlc3M9Yygic2NhbGUiLCAiY2VudGVyIiksDQogICAgICAgICAgICAgICAgIHRyQ29udHJvbCA9IHRyYWluQ29udHJvbChtZXRob2QgPSAiY3YiLCBudW1iZXI9MTApLA0KICAgICAgICAgICAgICAgICB0dW5lR3JpZCA9IGRhdGEuZnJhbWUoZGVncmVlPTEsIHNjYWxlPTEsIEM9MSkpDQoNCnJlc3VsdGFkb19lbnRyZW5hbWllbnRvMyA8LSBwcmVkaWN0KG1vZGVsbzMsIGVudHJlbmFtaWVudG8pDQpyZXN1bHRhZG9fcHJ1ZWJhMyA8LSBwcmVkaWN0KG1vZGVsbzMsIHBydWViYSkNCg0KbWNyZTMgPC0gY29uZnVzaW9uTWF0cml4KHJlc3VsdGFkb19lbnRyZW5hbWllbnRvMywgZW50cmVuYW1pZW50byRjbGFzaWZpY2FjaW9uKQ0KbWNycDMgPC0gY29uZnVzaW9uTWF0cml4KHJlc3VsdGFkb19wcnVlYmEzLCBwcnVlYmEkY2xhc2lmaWNhY2lvbikNCg0KDQpgYGANCiMjPHNwYW4gc3R5bGU9ICJjb2xvcjogYmx1ZTsiPk1vZGVsbyA0OiDDgXJib2wgZGUgRGVjaXNpw7NuPC9zcGFuPg0KYGBge3J9DQoNCm1vZGVsbzQgPC0gdHJhaW4oY2xhc2lmaWNhY2lvbiB+IE11cmRlciArIEFzc2F1bHQgKyBSYXBlLCBkYXRhPWVudHJlbmFtaWVudG8sDQogICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJycGFydCIsDQogICAgICAgICAgICAgICAgIHByZVByb2Nlc3M9Yygic2NhbGUiLCAiY2VudGVyIiksDQogICAgICAgICAgICAgICAgIHRyQ29udHJvbCA9IHRyYWluQ29udHJvbChtZXRob2QgPSAiY3YiLCBudW1iZXI9MTApLA0KICAgICAgICAgICAgICAgICB0dW5lTGVuZ3RoID0gMjAsICAjIEF1bWVudGEgbGEgbG9uZ2l0dWQgcGFyYSBtw6FzIHByb2Z1bmRpZGFkDQogICAgICAgICAgICAgICAgIGNvbnRyb2wgPSBycGFydC5jb250cm9sKG1pbnNwbGl0PTIsIGNwPTAuMDEsIG1heGRlcHRoPTUpICMgUGVybWl0ZSBtw6FzIGRpdmlzaW9uZXMNCikNCg0KcmVzdWx0YWRvX2VudHJlbmFtaWVudG80IDwtIHByZWRpY3QobW9kZWxvNCwgZW50cmVuYW1pZW50bykNCnJlc3VsdGFkb19wcnVlYmE0IDwtIHByZWRpY3QobW9kZWxvNCwgcHJ1ZWJhKQ0KDQptY3JlNCA8LSBjb25mdXNpb25NYXRyaXgocmVzdWx0YWRvX2VudHJlbmFtaWVudG80LCBlbnRyZW5hbWllbnRvJGNsYXNpZmljYWNpb24pDQptY3JwNCA8LSBjb25mdXNpb25NYXRyaXgocmVzdWx0YWRvX3BydWViYTQsIHBydWViYSRjbGFzaWZpY2FjaW9uKQ0KDQpycGFydC5wbG90KG1vZGVsbzQkZmluYWxNb2RlbCwgdHlwZT01LCBleHRyYT0xMDQsIGZhbGxlbi5sZWF2ZXM9VFJVRSwgYm94LnBhbGV0dGU9IkJsdWVzIikNCg0KDQpgYGANCiMjPHNwYW4gc3R5bGU9ICJjb2xvcjogYmx1ZTsiPk1vZGVsbyA1OiBSZWRlcyBOZXVyb25hbGVzPC9zcGFuPg0KYGBge3J9DQptb2RlbG81IDwtIHRyYWluKGNsYXNpZmljYWNpb24gfiBNdXJkZXIgKyBBc3NhdWx0ICsgUmFwZSwgZGF0YT1lbnRyZW5hbWllbnRvLA0KICAgICAgICAgICAgICAgICBtZXRob2QgPSAibm5ldCIsDQogICAgICAgICAgICAgICAgIHByZVByb2Nlc3M9Yygic2NhbGUiLCAiY2VudGVyIiksDQogICAgICAgICAgICAgICAgIHRyQ29udHJvbCA9IHRyYWluQ29udHJvbChtZXRob2QgPSAiY3YiLCBudW1iZXI9MTApLA0KICAgICAgICAgICAgICAgICB0cmFjZT1GQUxTRSkgDQoNCnJlc3VsdGFkb19lbnRyZW5hbWllbnRvNSA8LSBwcmVkaWN0KG1vZGVsbzUsIGVudHJlbmFtaWVudG8pDQpyZXN1bHRhZG9fcHJ1ZWJhNSA8LSBwcmVkaWN0KG1vZGVsbzUsIHBydWViYSkNCg0KbWNyZTUgPC0gY29uZnVzaW9uTWF0cml4KHJlc3VsdGFkb19lbnRyZW5hbWllbnRvNSwgZW50cmVuYW1pZW50byRjbGFzaWZpY2FjaW9uKQ0KbWNycDUgPC0gY29uZnVzaW9uTWF0cml4KHJlc3VsdGFkb19wcnVlYmE1LCBwcnVlYmEkY2xhc2lmaWNhY2lvbikNCg0KDQpgYGANCiMjPHNwYW4gc3R5bGU9ICJjb2xvcjogYmx1ZTsiPk1vZGVsbyA2OiBSYW5kb20gRm9yZXN0PC9zcGFuPg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCm1vZGVsbzYgPC0gdHJhaW4oY2xhc2lmaWNhY2lvbiB+IE11cmRlciArIEFzc2F1bHQgKyBSYXBlLCBkYXRhPWVudHJlbmFtaWVudG8sDQogICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJyZiIsDQogICAgICAgICAgICAgICAgIHByZVByb2Nlc3M9Yygic2NhbGUiLCAiY2VudGVyIiksDQogICAgICAgICAgICAgICAgIHRyQ29udHJvbCA9IHRyYWluQ29udHJvbChtZXRob2QgPSAiY3YiLCBudW1iZXI9MTApLA0KICAgICAgICAgICAgICAgICB0dW5lR3JpZCA9IGV4cGFuZC5ncmlkKG10cnkgPSBjKDIsIDQsIDYpKSkgDQoNCnJlc3VsdGFkb19lbnRyZW5hbWllbnRvNiA8LSBwcmVkaWN0KG1vZGVsbzYsIGVudHJlbmFtaWVudG8pDQpyZXN1bHRhZG9fcHJ1ZWJhNiA8LSBwcmVkaWN0KG1vZGVsbzYsIHBydWViYSkNCg0KbWNyZTYgPC0gY29uZnVzaW9uTWF0cml4KHJlc3VsdGFkb19lbnRyZW5hbWllbnRvNiwgZW50cmVuYW1pZW50byRjbGFzaWZpY2FjaW9uKQ0KbWNycDYgPC0gY29uZnVzaW9uTWF0cml4KHJlc3VsdGFkb19wcnVlYmE2LCBwcnVlYmEkY2xhc2lmaWNhY2lvbikNCg0KDQpgYGANCiMjPHNwYW4gc3R5bGU9ICJjb2xvcjogYmx1ZTsiPiBSZXN1bWVuIGRlIFJlc3VsdGFkb3M8L3NwYW4+DQpgYGB7cn0NCnJlc3VsdGFkb3MgPC0gZGF0YS5mcmFtZSgNCiAgIlNWTSBMaW5lYWwiID0gYyhtY3JlMSRvdmVyYWxsWyJBY2N1cmFjeSJdLCBtY3JwMSRvdmVyYWxsWyJBY2N1cmFjeSJdKSwNCiAgIlNWTSBSYWRpYWwiID0gYyhtY3JlMiRvdmVyYWxsWyJBY2N1cmFjeSJdLCBtY3JwMiRvdmVyYWxsWyJBY2N1cmFjeSJdKSwNCiAgIlNWTSBQb2xpbsOzbWljbyIgPSBjKG1jcmUzJG92ZXJhbGxbIkFjY3VyYWN5Il0sIG1jcnAzJG92ZXJhbGxbIkFjY3VyYWN5Il0pLA0KICAiw4FyYm9sIGRlIGRlY2lzacOzbiIgPSBjKG1jcmU0JG92ZXJhbGxbIkFjY3VyYWN5Il0sIG1jcnA0JG92ZXJhbGxbIkFjY3VyYWN5Il0pLA0KICAiUmVkZXMgTmV1cm9uYWxlcyIgPSBjKG1jcmU1JG92ZXJhbGxbIkFjY3VyYWN5Il0sIG1jcnA1JG92ZXJhbGxbIkFjY3VyYWN5Il0pLA0KICAiQm9zcXVlcyBBbGVhdG9yaW9zIiA9IGMobWNyZTYkb3ZlcmFsbFsiQWNjdXJhY3kiXSwgbWNycDYkb3ZlcmFsbFsiQWNjdXJhY3kiXSkNCikNCg0Kcm93bmFtZXMocmVzdWx0YWRvcykgPC0gYygiUHJlY2lzacOzbiBkZSBFbnRyZW5hbWllbnRvIiwgIlByZWNpc2nDs24gZGUgUHJ1ZWJhIikNCnJlc3VsdGFkb3MNCg0KDQpgYGANCg0K