Análisis de Clusters Individuales

Cargar y preparar los datos

# Paso 1. Paquetes
pkgs <- c("readxl","dplyr","stringr","ggplot2","factoextra")

# Verifica cuáles faltan
to_install <- pkgs[!(pkgs %in% rownames(installed.packages()))]

# Instala los que falten
if(length(to_install) > 0) install.packages(to_install)

# Carga los paquetes
library(readxl)
library(dplyr)
## 
## Adjuntando el paquete: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
library(stringr)
library(ggplot2)
library(factoextra)
## Welcome! Want to learn more? See two factoextra-related books at https://goo.gl/ve3WBa

Selección de variables para clustering

# 2A: detectar fila de encabezados
raw0 <- read_excel("D:\\Anton V2\\Downloads\\vmrc.xlsx", col_names = FALSE)
## New names:
## • `` -> `...1`
## • `` -> `...2`
## • `` -> `...3`
## • `` -> `...4`
## • `` -> `...5`
## • `` -> `...6`
## • `` -> `...7`
## • `` -> `...8`
## • `` -> `...9`
## • `` -> `...10`
## • `` -> `...11`
## • `` -> `...12`
## • `` -> `...13`
## • `` -> `...14`
## • `` -> `...15`
## • `` -> `...16`
## • `` -> `...17`
## • `` -> `...18`
## • `` -> `...19`
## • `` -> `...20`
## • `` -> `...21`
## • `` -> `...22`
header_row <- which(apply(raw0, 1, function(r) any(grepl("ENTIDAD\\s*FEDERATIVA", r, TRUE))))[1]

# 2B: recargar con encabezado correcto
raw <- read_excel("D:\\Anton V2\\Downloads\\vmrc.xlsx", skip = header_row - 1)
## New names:
## • `` -> `...3`
## • `` -> `...5`
## • `` -> `...6`
## • `` -> `...7`
## • `` -> `...8`
## • `` -> `...10`
## • `` -> `...11`
## • `` -> `...12`
## • `` -> `...13`
## • `` -> `...15`
## • `` -> `...16`
## • `` -> `...17`
## • `` -> `...18`
## • `` -> `...20`
## • `` -> `...21`
## • `` -> `...22`
# normalizar nombres
nms <- names(raw)
nms <- iconv(nms, from = "", to = "ASCII//TRANSLIT")
nms <- str_replace_all(nms, "\\s+", "_")
nms <- str_to_lower(nms)
names(raw) <- nms

# quedarnos con Estado y Total
df <- raw %>%
  select(Estado = entidad_federativa, Total = total) %>%
  mutate(Estado = as.character(Estado),
         Total  = suppressWarnings(as.numeric(Total))) %>%
  filter(!is.na(Estado), !is.na(Total)) %>%
  filter(!grepl("^total$", Estado, TRUE),
         !grepl("estados\\s+unidos\\s+mexicanos", Estado, TRUE))

head(df)
## # A tibble: 6 × 2
##   Estado                Total
##   <chr>                 <dbl>
## 1 Aguascalientes       756781
## 2 Baja California     2404000
## 3 Baja California Sur  624470
## 4 Campeche             404759
## 5 Chiapas             1231890
## 6 Chihuahua           1879130

Escalamiento y normalización

variable_base <- "Total"   # no hay población en este archivo
x   <- scale(df[[variable_base]])
mat <- as.matrix(x)        # para factoextra y dist

Método de agrupamiento elegido

fviz_nbclust(mat, kmeans, method="wss") + ggtitle("Método del codo (WSS)")

fviz_nbclust(mat, kmeans, method="silhouette") + ggtitle("Coeficiente de silueta")

if (!exists("k_elegido")) k_elegido <- 3
k_elegido
## [1] 3

Formación de clusters individuales

set.seed(123)
km <- kmeans(mat, centers = k_elegido, nstart = 50)

df_km <- df %>% mutate(cluster_kmeans = factor(km$cluster))
km$centers
##         [,1]
## 1 -0.4435732
## 2  3.3224750
## 3  0.2966763
df_km %>% arrange(cluster_kmeans, .data[[variable_base]])
## # A tibble: 32 × 3
##    Estado               Total cluster_kmeans
##    <chr>                <dbl> <fct>         
##  1 Campeche            404759 1             
##  2 Colima              427510 1             
##  3 Nayarit             556713 1             
##  4 Baja California Sur 624470 1             
##  5 Tlaxcala            638165 1             
##  6 Hidalgo             707339 1             
##  7 Zacatecas           736225 1             
##  8 Aguascalientes      756781 1             
##  9 Durango             765038 1             
## 10 Tabasco             798316 1             
## # ℹ 22 more rows

Visualización de los clusters

df_plot <- df_km %>%
  arrange(.data[[variable_base]]) %>%
  mutate(Estado = factor(Estado, levels = Estado))

ggplot(df_plot, aes(x = Estado, y = .data[[variable_base]], fill = cluster_kmeans)) +
  geom_col() + coord_flip() +
  labs(title = "Total de vehículos por estado (clusters K-means)",
       x = "Estado", y = "Total de vehículos", fill = "Cluster") +
  theme_minimal()

ggplot(df_km, aes(x = cluster_kmeans, y = .data[[variable_base]], fill = cluster_kmeans)) +
  geom_boxplot(alpha=.85) +
  labs(title = "Distribución por cluster (K-means)",
       x = "Cluster", y = "Total de vehículos") +
  theme_minimal() + guides(fill="none")

# Checa el tamaño y si hay problemas de NA/duplicados
nrow(df); length(unique(df$Estado))
## [1] 32
## [1] 32
summary(df$Total)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##  404759  762974 1263658 1818728 1925416 9988109
sum(!is.finite(df$Total))
## [1] 0
head(df, 10)
## # A tibble: 10 × 2
##    Estado                 Total
##    <chr>                  <dbl>
##  1 Aguascalientes        756781
##  2 Baja California      2404000
##  3 Baja California Sur   624470
##  4 Campeche              404759
##  5 Chiapas              1231890
##  6 Chihuahua            1879130
##  7 Ciudad de México     6471738
##  8 Coahuila de Zaragoza 1257493
##  9 Colima                427510
## 10 Durango               765038
# Crear df_fix a partir de df
df_fix <- df   # o el nombre de tu dataset original
cat("Número de estados en df_fix:", nrow(df_fix), "\n")
## Número de estados en df_fix: 32
print(df_fix)
## # A tibble: 32 × 2
##    Estado                 Total
##    <chr>                  <dbl>
##  1 Aguascalientes        756781
##  2 Baja California      2404000
##  3 Baja California Sur   624470
##  4 Campeche              404759
##  5 Chiapas              1231890
##  6 Chihuahua            1879130
##  7 Ciudad de México     6471738
##  8 Coahuila de Zaragoza 1257493
##  9 Colima                427510
## 10 Durango               765038
## # ℹ 22 more rows

Interpretación de resultados

# 1) Usa el data frame correcto
df <- df_fix    # asegúrate de apuntar al df que sí tiene los 32 estados

# 2) Limpieza por si acaso
df <- df |>
  dplyr::filter(!is.na(Total), is.finite(Total)) |>
  dplyr::mutate(Estado = trimws(Estado)) |>
  dplyr::distinct(Estado, .keep_all = TRUE)

# 3) Verificación
cat("Filas válidas:", nrow(df), "\n")  # Debe ser >= 2 (ideal: 32)
## Filas válidas: 32
print(head(df, 10))
## # A tibble: 10 × 2
##    Estado                 Total
##    <chr>                  <dbl>
##  1 Aguascalientes        756781
##  2 Baja California      2404000
##  3 Baja California Sur   624470
##  4 Campeche              404759
##  5 Chiapas              1231890
##  6 Chihuahua            1879130
##  7 Ciudad de México     6471738
##  8 Coahuila de Zaragoza 1257493
##  9 Colima                427510
## 10 Durango               765038
# 4) Matriz 1-columna + nombres de fila
mat <- as.matrix(df$Total)
rownames(mat) <- df$Estado

# 5) Heatmap (solo clusteriza FILAS; no columnas)
if (!require(pheatmap)) install.packages("pheatmap")
## Cargando paquete requerido: pheatmap
library(pheatmap)

pheatmap(
  mat,
  scale = "column",                 # correcto para 1 variable
  cluster_rows = TRUE,              # dendrograma de estados
  cluster_cols = FALSE,             # no intentes clusterizar columnas (solo hay 1)
  clustering_method = "ward.D2",
  main = "Mapa de calor con dendrograma — Clustering jerárquico",
  color = colorRampPalette(c("blue","white","red"))(50),
  fontsize_row = 8
)

# Conclusiones del análisis

d  <- dist(mat, method = "euclidean")
hc <- hclust(d, method = "complete")
plot(hc, main = "Dendrograma - Clustering jerárquico", xlab = "Estados")
rect.hclust(hc, k = k_elegido, border = "red")

df_hc <- df %>% mutate(cluster_hclust = factor(cutree(hc, k = k_elegido)))
df_hc %>% arrange(cluster_hclust, .data[[variable_base]])
## # A tibble: 32 × 3
##    Estado               Total cluster_hclust
##    <chr>                <dbl> <fct>         
##  1 Campeche            404759 1             
##  2 Colima              427510 1             
##  3 Nayarit             556713 1             
##  4 Baja California Sur 624470 1             
##  5 Tlaxcala            638165 1             
##  6 Hidalgo             707339 1             
##  7 Zacatecas           736225 1             
##  8 Aguascalientes      756781 1             
##  9 Durango             765038 1             
## 10 Tabasco             798316 1             
## # ℹ 22 more rows
LS0tDQp0aXRsZTogIkNsdXN0ZXJzIGluZGl2aWR1YWxlcyINCmF1dGhvcjogIkFudG9uaW8gQ8OhcmRlbmFzIER1YXJ0ZSAtIEEwMTU2ODkwOCINCmRhdGU6ICIyMDI1LTA4LTI5Ig0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIHRvYzogdHJ1ZQ0KICAgIHRvY19mbG9hdDogdHJ1ZQ0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgICB0aGVtZTogcGFwZXINCi0tLQ0KDQojIDxzcGFuIHN0eWxlPSJjb2xvcjogZGFya2JsdWU7IGZvbnQtc2l6ZTogMjhweDsiPiBBbsOhbGlzaXMgZGUgQ2x1c3RlcnMgSW5kaXZpZHVhbGVzIDwvc3Bhbj4NCiAgICANCiAgICANCiFbXShodHRwczovL2kuZ2lmZXIuY29tL0dJVW8uZ2lmKQ0KLS0tDQojIDxzcGFuIHN0eWxlPSJjb2xvcjogYmx1ZTsiPiBDYXJnYXIgeSBwcmVwYXJhciBsb3MgZGF0b3MgPC9zcGFuPg0KYGBge3J9DQojIFBhc28gMS4gUGFxdWV0ZXMNCnBrZ3MgPC0gYygicmVhZHhsIiwiZHBseXIiLCJzdHJpbmdyIiwiZ2dwbG90MiIsImZhY3RvZXh0cmEiKQ0KDQojIFZlcmlmaWNhIGN1w6FsZXMgZmFsdGFuDQp0b19pbnN0YWxsIDwtIHBrZ3NbIShwa2dzICVpbiUgcm93bmFtZXMoaW5zdGFsbGVkLnBhY2thZ2VzKCkpKV0NCg0KIyBJbnN0YWxhIGxvcyBxdWUgZmFsdGVuDQppZihsZW5ndGgodG9faW5zdGFsbCkgPiAwKSBpbnN0YWxsLnBhY2thZ2VzKHRvX2luc3RhbGwpDQoNCiMgQ2FyZ2EgbG9zIHBhcXVldGVzDQpsaWJyYXJ5KHJlYWR4bCkNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KHN0cmluZ3IpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KGZhY3RvZXh0cmEpDQoNCmBgYA0KDQojIDxzcGFuIHN0eWxlPSJjb2xvcjogYmx1ZTsiPiBTZWxlY2Npw7NuIGRlIHZhcmlhYmxlcyBwYXJhIGNsdXN0ZXJpbmcgPC9zcGFuPg0KDQpgYGB7cn0NCiMgMkE6IGRldGVjdGFyIGZpbGEgZGUgZW5jYWJlemFkb3MNCnJhdzAgPC0gcmVhZF9leGNlbCgiRDpcXEFudG9uIFYyXFxEb3dubG9hZHNcXHZtcmMueGxzeCIsIGNvbF9uYW1lcyA9IEZBTFNFKQ0KaGVhZGVyX3JvdyA8LSB3aGljaChhcHBseShyYXcwLCAxLCBmdW5jdGlvbihyKSBhbnkoZ3JlcGwoIkVOVElEQURcXHMqRkVERVJBVElWQSIsIHIsIFRSVUUpKSkpWzFdDQoNCiMgMkI6IHJlY2FyZ2FyIGNvbiBlbmNhYmV6YWRvIGNvcnJlY3RvDQpyYXcgPC0gcmVhZF9leGNlbCgiRDpcXEFudG9uIFYyXFxEb3dubG9hZHNcXHZtcmMueGxzeCIsIHNraXAgPSBoZWFkZXJfcm93IC0gMSkNCg0KIyBub3JtYWxpemFyIG5vbWJyZXMNCm5tcyA8LSBuYW1lcyhyYXcpDQpubXMgPC0gaWNvbnYobm1zLCBmcm9tID0gIiIsIHRvID0gIkFTQ0lJLy9UUkFOU0xJVCIpDQpubXMgPC0gc3RyX3JlcGxhY2VfYWxsKG5tcywgIlxccysiLCAiXyIpDQpubXMgPC0gc3RyX3RvX2xvd2VyKG5tcykNCm5hbWVzKHJhdykgPC0gbm1zDQoNCiMgcXVlZGFybm9zIGNvbiBFc3RhZG8geSBUb3RhbA0KZGYgPC0gcmF3ICU+JQ0KICBzZWxlY3QoRXN0YWRvID0gZW50aWRhZF9mZWRlcmF0aXZhLCBUb3RhbCA9IHRvdGFsKSAlPiUNCiAgbXV0YXRlKEVzdGFkbyA9IGFzLmNoYXJhY3RlcihFc3RhZG8pLA0KICAgICAgICAgVG90YWwgID0gc3VwcHJlc3NXYXJuaW5ncyhhcy5udW1lcmljKFRvdGFsKSkpICU+JQ0KICBmaWx0ZXIoIWlzLm5hKEVzdGFkbyksICFpcy5uYShUb3RhbCkpICU+JQ0KICBmaWx0ZXIoIWdyZXBsKCJedG90YWwkIiwgRXN0YWRvLCBUUlVFKSwNCiAgICAgICAgICFncmVwbCgiZXN0YWRvc1xccyt1bmlkb3NcXHMrbWV4aWNhbm9zIiwgRXN0YWRvLCBUUlVFKSkNCg0KaGVhZChkZikNCmBgYA0KIyA8c3BhbiBzdHlsZT0iY29sb3I6IGJsdWU7Ij4gRXNjYWxhbWllbnRvIHkgbm9ybWFsaXphY2nDs24gPC9zcGFuPg0KDQpgYGB7cn0NCnZhcmlhYmxlX2Jhc2UgPC0gIlRvdGFsIiAgICMgbm8gaGF5IHBvYmxhY2nDs24gZW4gZXN0ZSBhcmNoaXZvDQp4ICAgPC0gc2NhbGUoZGZbW3ZhcmlhYmxlX2Jhc2VdXSkNCm1hdCA8LSBhcy5tYXRyaXgoeCkgICAgICAgICMgcGFyYSBmYWN0b2V4dHJhIHkgZGlzdA0KDQpgYGANCiMgPHNwYW4gc3R5bGU9ImNvbG9yOiBibHVlOyI+IE3DqXRvZG8gZGUgYWdydXBhbWllbnRvIGVsZWdpZG8gPC9zcGFuPg0KDQpgYGB7cn0NCmZ2aXpfbmJjbHVzdChtYXQsIGttZWFucywgbWV0aG9kPSJ3c3MiKSArIGdndGl0bGUoIk3DqXRvZG8gZGVsIGNvZG8gKFdTUykiKQ0KDQpgYGANCg0KYGBge3J9DQpmdml6X25iY2x1c3QobWF0LCBrbWVhbnMsIG1ldGhvZD0ic2lsaG91ZXR0ZSIpICsgZ2d0aXRsZSgiQ29lZmljaWVudGUgZGUgc2lsdWV0YSIpDQoNCmBgYA0KDQpgYGB7cn0NCmlmICghZXhpc3RzKCJrX2VsZWdpZG8iKSkga19lbGVnaWRvIDwtIDMNCmtfZWxlZ2lkbw0KDQpgYGANCiMgPHNwYW4gc3R5bGU9ImNvbG9yOiBibHVlOyI+IEZvcm1hY2nDs24gZGUgY2x1c3RlcnMgaW5kaXZpZHVhbGVzIDwvc3Bhbj4NCg0KYGBge3J9DQpzZXQuc2VlZCgxMjMpDQprbSA8LSBrbWVhbnMobWF0LCBjZW50ZXJzID0ga19lbGVnaWRvLCBuc3RhcnQgPSA1MCkNCg0KZGZfa20gPC0gZGYgJT4lIG11dGF0ZShjbHVzdGVyX2ttZWFucyA9IGZhY3RvcihrbSRjbHVzdGVyKSkNCmttJGNlbnRlcnMNCmRmX2ttICU+JSBhcnJhbmdlKGNsdXN0ZXJfa21lYW5zLCAuZGF0YVtbdmFyaWFibGVfYmFzZV1dKQ0KDQpgYGANCiMgPHNwYW4gc3R5bGU9ImNvbG9yOiBibHVlOyI+IFZpc3VhbGl6YWNpw7NuIGRlIGxvcyBjbHVzdGVycyA8L3NwYW4+DQoNCmBgYHtyfQ0KZGZfcGxvdCA8LSBkZl9rbSAlPiUNCiAgYXJyYW5nZSguZGF0YVtbdmFyaWFibGVfYmFzZV1dKSAlPiUNCiAgbXV0YXRlKEVzdGFkbyA9IGZhY3RvcihFc3RhZG8sIGxldmVscyA9IEVzdGFkbykpDQoNCmdncGxvdChkZl9wbG90LCBhZXMoeCA9IEVzdGFkbywgeSA9IC5kYXRhW1t2YXJpYWJsZV9iYXNlXV0sIGZpbGwgPSBjbHVzdGVyX2ttZWFucykpICsNCiAgZ2VvbV9jb2woKSArIGNvb3JkX2ZsaXAoKSArDQogIGxhYnModGl0bGUgPSAiVG90YWwgZGUgdmVow61jdWxvcyBwb3IgZXN0YWRvIChjbHVzdGVycyBLLW1lYW5zKSIsDQogICAgICAgeCA9ICJFc3RhZG8iLCB5ID0gIlRvdGFsIGRlIHZlaMOtY3Vsb3MiLCBmaWxsID0gIkNsdXN0ZXIiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3QoZGZfa20sIGFlcyh4ID0gY2x1c3Rlcl9rbWVhbnMsIHkgPSAuZGF0YVtbdmFyaWFibGVfYmFzZV1dLCBmaWxsID0gY2x1c3Rlcl9rbWVhbnMpKSArDQogIGdlb21fYm94cGxvdChhbHBoYT0uODUpICsNCiAgbGFicyh0aXRsZSA9ICJEaXN0cmlidWNpw7NuIHBvciBjbHVzdGVyIChLLW1lYW5zKSIsDQogICAgICAgeCA9ICJDbHVzdGVyIiwgeSA9ICJUb3RhbCBkZSB2ZWjDrWN1bG9zIikgKw0KICB0aGVtZV9taW5pbWFsKCkgKyBndWlkZXMoZmlsbD0ibm9uZSIpDQoNCmBgYA0KDQpgYGB7cn0NCiMgQ2hlY2EgZWwgdGFtYcOxbyB5IHNpIGhheSBwcm9ibGVtYXMgZGUgTkEvZHVwbGljYWRvcw0KbnJvdyhkZik7IGxlbmd0aCh1bmlxdWUoZGYkRXN0YWRvKSkNCnN1bW1hcnkoZGYkVG90YWwpDQpzdW0oIWlzLmZpbml0ZShkZiRUb3RhbCkpDQpoZWFkKGRmLCAxMCkNCg0KYGBgDQpgYGB7cn0NCiMgQ3JlYXIgZGZfZml4IGEgcGFydGlyIGRlIGRmDQpkZl9maXggPC0gZGYgICAjIG8gZWwgbm9tYnJlIGRlIHR1IGRhdGFzZXQgb3JpZ2luYWwNCg0KYGBgDQoNCmBgYHtyfQ0KY2F0KCJOw7ptZXJvIGRlIGVzdGFkb3MgZW4gZGZfZml4OiIsIG5yb3coZGZfZml4KSwgIlxuIikNCnByaW50KGRmX2ZpeCkNCg0KYGBgDQojIDxzcGFuIHN0eWxlPSJjb2xvcjogYmx1ZTsiPiBJbnRlcnByZXRhY2nDs24gZGUgcmVzdWx0YWRvcyA8L3NwYW4+DQoNCmBgYHtyfQ0KIyAxKSBVc2EgZWwgZGF0YSBmcmFtZSBjb3JyZWN0bw0KZGYgPC0gZGZfZml4ICAgICMgYXNlZ8O6cmF0ZSBkZSBhcHVudGFyIGFsIGRmIHF1ZSBzw60gdGllbmUgbG9zIDMyIGVzdGFkb3MNCg0KIyAyKSBMaW1waWV6YSBwb3Igc2kgYWNhc28NCmRmIDwtIGRmIHw+DQogIGRwbHlyOjpmaWx0ZXIoIWlzLm5hKFRvdGFsKSwgaXMuZmluaXRlKFRvdGFsKSkgfD4NCiAgZHBseXI6Om11dGF0ZShFc3RhZG8gPSB0cmltd3MoRXN0YWRvKSkgfD4NCiAgZHBseXI6OmRpc3RpbmN0KEVzdGFkbywgLmtlZXBfYWxsID0gVFJVRSkNCg0KIyAzKSBWZXJpZmljYWNpw7NuDQpjYXQoIkZpbGFzIHbDoWxpZGFzOiIsIG5yb3coZGYpLCAiXG4iKSAgIyBEZWJlIHNlciA+PSAyIChpZGVhbDogMzIpDQpwcmludChoZWFkKGRmLCAxMCkpDQoNCiMgNCkgTWF0cml6IDEtY29sdW1uYSArIG5vbWJyZXMgZGUgZmlsYQ0KbWF0IDwtIGFzLm1hdHJpeChkZiRUb3RhbCkNCnJvd25hbWVzKG1hdCkgPC0gZGYkRXN0YWRvDQoNCiMgNSkgSGVhdG1hcCAoc29sbyBjbHVzdGVyaXphIEZJTEFTOyBubyBjb2x1bW5hcykNCmlmICghcmVxdWlyZShwaGVhdG1hcCkpIGluc3RhbGwucGFja2FnZXMoInBoZWF0bWFwIikNCmxpYnJhcnkocGhlYXRtYXApDQoNCnBoZWF0bWFwKA0KICBtYXQsDQogIHNjYWxlID0gImNvbHVtbiIsICAgICAgICAgICAgICAgICAjIGNvcnJlY3RvIHBhcmEgMSB2YXJpYWJsZQ0KICBjbHVzdGVyX3Jvd3MgPSBUUlVFLCAgICAgICAgICAgICAgIyBkZW5kcm9ncmFtYSBkZSBlc3RhZG9zDQogIGNsdXN0ZXJfY29scyA9IEZBTFNFLCAgICAgICAgICAgICAjIG5vIGludGVudGVzIGNsdXN0ZXJpemFyIGNvbHVtbmFzIChzb2xvIGhheSAxKQ0KICBjbHVzdGVyaW5nX21ldGhvZCA9ICJ3YXJkLkQyIiwNCiAgbWFpbiA9ICJNYXBhIGRlIGNhbG9yIGNvbiBkZW5kcm9ncmFtYSDigJQgQ2x1c3RlcmluZyBqZXLDoXJxdWljbyIsDQogIGNvbG9yID0gY29sb3JSYW1wUGFsZXR0ZShjKCJibHVlIiwid2hpdGUiLCJyZWQiKSkoNTApLA0KICBmb250c2l6ZV9yb3cgPSA4DQopDQoNCg0KYGBgDQojIDxzcGFuIHN0eWxlPSJjb2xvcjogYmx1ZTsiPiBDb25jbHVzaW9uZXMgZGVsIGFuw6FsaXNpcyA8L3NwYW4+DQoNCmBgYHtyfQ0KZCAgPC0gZGlzdChtYXQsIG1ldGhvZCA9ICJldWNsaWRlYW4iKQ0KaGMgPC0gaGNsdXN0KGQsIG1ldGhvZCA9ICJjb21wbGV0ZSIpDQpwbG90KGhjLCBtYWluID0gIkRlbmRyb2dyYW1hIC0gQ2x1c3RlcmluZyBqZXLDoXJxdWljbyIsIHhsYWIgPSAiRXN0YWRvcyIpDQpyZWN0LmhjbHVzdChoYywgayA9IGtfZWxlZ2lkbywgYm9yZGVyID0gInJlZCIpDQoNCmRmX2hjIDwtIGRmICU+JSBtdXRhdGUoY2x1c3Rlcl9oY2x1c3QgPSBmYWN0b3IoY3V0cmVlKGhjLCBrID0ga19lbGVnaWRvKSkpDQpkZl9oYyAlPiUgYXJyYW5nZShjbHVzdGVyX2hjbHVzdCwgLmRhdGFbW3ZhcmlhYmxlX2Jhc2VdXSkNCg0KYGBgDQo=