0. Preparación del ambiente de trabajo

0.1. Idioma

Es importante cambiar el idioma con el que se le pide al R que detecte y lea el contenido de los data.frame. Para cambiarlo a español se puede utilizar el siguiente comando:

Sys.setlocale("LC_ALL", "en_US.UTF-8")
## [1] "en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/C"

Una alternativa es:

Sys.setenv(LANG = "spa")

0.2. Librerías

Se utilizarán las librerías “readxl”, “tidyverse”, “pvclust”, “mclust”, “cluster” y “fpc”.  Debido a que se trata de varias librerías, también se puede utilizar la librería easypackages() para hacerlo simultáneamente.

Para instalar dichas librerías, en caso de no contar con ellas previamente, se utiliza el comando install.packages() de la siguiente manera:

install.packages("readxl")
install.packages("tidyverse")
install.packages("pvclust")
install.packages("mclust")
install.packages("cluster")
install.packages("fpc")
install.packages("esasypackages")

Ahora, para instalarlas simultáneamente:

library(easypackages) #activación de librería
paquetes <- c("readxl", "tidyverse", "pvclust", "mclust", "cluster", "fpc")
libraries(paquetes)
## Loading required package: readxl
## Loading required package: tidyverse
## ── Attaching packages ─────────────────────────────────────── tidyverse 1.3.1 ──
## ✓ ggplot2 3.3.5     ✓ purrr   0.3.4
## ✓ tibble  3.1.6     ✓ dplyr   1.0.8
## ✓ tidyr   1.2.0     ✓ stringr 1.4.0
## ✓ readr   2.1.2     ✓ forcats 0.5.1
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## x dplyr::filter() masks stats::filter()
## x dplyr::lag()    masks stats::lag()
## Loading required package: pvclust
## Loading required package: mclust
## Package 'mclust' version 5.4.9
## Type 'citation("mclust")' for citing this R package in publications.
## 
## Attaching package: 'mclust'
## The following object is masked from 'package:purrr':
## 
##     map
## Loading required package: cluster
## Loading required package: fpc
## All packages loaded successfully

0.3 Carga de datos

En este tutorial se utilizará la base de datos del Índice de Desarrollo Humano generada por el PNUD para el caso de México, agregado a nivel municipal para los años 2000 y 2005.
Se puede descargar la base de datos se puede hacerlo desde este perfil de GitHub

Una vez que se ha descargado y descomprimido la matriz de datos, esta deberá cargarse en R Studio:

datos_idh <- read.csv("~/Dropbox/R/idh_mpio_2000_2005.csv")

0.4. Revisión de la estructura del data.frame

Antes de avanzar a realizar cualquier trabajo con los datos contenidos en la matriz, es importante revisar la estructura de las variables del data frame con str() para, así, identificar la dimensión de la matriz de datos, la manera en que el software identifica los nombres de las variables, así como el tipo de variable con que reconoce a cada una y, además, un vistazo a los valores de cada variable.

str(datos_idh)
## 'data.frame':    2454 obs. of  22 variables:
##  $ id_mpio                      : int  9014 19019 20350 9016 9003 19046 8019 28009 15054 15020 ...
##  $ entidad                      : chr  "Distrito Federal" "Nuevo León" "Oaxaca" "Distrito Federal" ...
##  $ mpio                         : chr  "Benito Juárez" "San Pedro Garza García" "San Sebastián Tutla" "Miguel Hidalgo" ...
##  $ clasificacion_2000           : int  1 2 16 4 3 6 13 21 9 20 ...
##  $ grado_idh_2005               : chr  "alto" "alto" "alto" "alto" ...
##  $ idh_2000                     : num  0.916 0.892 0.854 0.882 0.884 ...
##  $ clasificacion_2005           : int  1 2 3 4 5 6 7 8 9 10 ...
##  $ idh_2005                     : num  0.951 0.95 0.92 0.919 0.917 ...
##  $ tasa_moralidad_infantil_2000 : num  17.6 18.4 18 19.1 18.8 ...
##  $ tasa_mortalidad_infantil_2005: num  3.02 3.19 5.28 7.3 6.96 ...
##  $ tasa_alfabetizacion_2000     : num  98.9 97.9 97.3 97.9 97.5 ...
##  $ tasa_alfabetizacion_2005     : num  97.7 98.3 98.5 97.9 97.9 ...
##  $ tasa_asistencia_escolar_2000 : num  77.4 65.3 80.5 70.6 73.6 ...
##  $ tasa_asistencia_escolar_2005 : num  78.7 67.4 81.8 73.1 75.1 ...
##  $ usd_ppc_2000                 : num  31182 27914 10349 21290 20911 ...
##  $ usd_ppc_2005                 : num  27824 33813 16441 21549 19724 ...
##  $ indice_salud_2000            : num  0.874 0.868 0.871 0.862 0.864 ...
##  $ indice_salud_2005            : num  1 0.998 0.98 0.963 0.966 ...
##  $ indice_educacion_2000        : num  0.917 0.87 0.917 0.888 0.895 ...
##  $ indice_educacion_2005        : num  0.914 0.88 0.929 0.897 0.903 ...
##  $ indice_ingreso_2000          : num  0.958 0.94 0.774 0.895 0.892 ...
##  $ indice_ingreso_2005          : num  0.939 0.972 0.852 0.897 0.882 ...

1. Presentación al Análisis de Conglomerados

El análisis de conglomerados (o cluster analysis) es una técnica estadística que permite la clasificación de los casos, en la que se intenta agrupar a los casos con características similares entre sí, mientras que se busca que los grupos creados sean diferentes entre sí. Esto permite que objetos (data.frames o matrices de datos) grandes sean “descompuestos” a su vez en objetos más pequeños cuidando, así, que los casos contenidos dentro de cada grupo sean semejantes con respecto de otros casos contenidos en otros subgrupos.

La integración de clusters se puede entender como una estrategia para resolver problemas de clasificación de los casos, a partir de tomar en cuenta un conjunto de variables comunes. Sin embargo, uno de los problemas comunes de esta técnica radica en la identificación de la cantidad de clusters en que se deben integrar los casos, pues existen distintas maneras para lograrlo y, no necesariamente, todas arriban al mismo resultado. 

En este sentido, el análisis de conglomerados no consiste en un algoritmo único que arroje un resultado común, sino que es una estrategia de “resolución de problemas” que, además, abre la puerta para la intervención de la subjetividad del analista.

Existe una gran cantidad de estrategias para implementar un análisis de conglomerados, las que dependen de los tipos de algoritmos para realizar dichos agrupamientos. Estos tipos se pueden caracterizar por los siguientes principios o criterios de análisis de los datos:

  1. Modelos de conectividad: Estos modelos clasifican los casos en clusters basados en la distancia existente entre cada punto o caso. El principio básico que lo guía consiste en que los objetos más cercanos entre sí serán más similares respecto de otros objetos que se encuentren más alejados entre sí. A su vez, estos modelos tienen dos acercamientos a la construcción de los clusters:

    • El primer acercamiento consiste en dividir todos los puntos en cluster y, después, agregarlos mientras que sus distancias se incrementan.
    • El segundo enfoque consiste en, primero, integrar a todos los casos en un mismo cluster y, posteriormente, dividirlos en otros agrupamientos a medida que sus distancias se incrementa. Asimismo, las distancias pueden variar a partir del algoritmo utilizado para medirlas, ya sean, por ejemplo, métodos euclidianos o manhattan (como los más comunes).
  2. Modelos basados en centroides: Estos se apoyan en algoritmos iterativos para generar agrupamientos o clusters. La noción de similaridad se deriva de la distancia de cada caso respecto de un centroide para cada uno de los clusters que se generan. Estos algoritmos requieren, de antemano, la especificación del número de clusters a implementar. Y con cada iteración, se corrige la posición del centroide de cada cluster, a la vez que se ajusta la clasificación y asignación de cada caso a los diversos clusters.

  3. Modelos basados en distribuciones: Estos modelos difieren de otros tipos métodos de agrupamiento debido a que consideran la probabilidad de pertenencia de cada caso a cada cluster, en lugar de tomar en consideración las distancias entre datos. Estos modelos suelen sufrir de sobre-ajuste.

  4. Modelos de densidad: Estos modelos consideran la densidad de los puntos o casos en diferentes partes del espacio para, así, crear clusters en los subespacios con densidades similares. Suelen aislar varios subespacios basado en la densidad de cada punto de información presente y asignan a los casos entre los clusters separados.

En este tutorial se revisarán 3 técnicas para el análisis de clusters, que integran a algunos de estos tipos de criterios de conglomeración:

  1. Método de k-medias(partitioning k-means).
  2. Método jerárquico (hierarchical agglomerative).
  3. Métodos basados en modelos.

2. Método k-medias (k-means)

El método de partición o k-medias (partitioning k-means) trabaja con la lógica de los modelos iterativos con centroides, y trabaja a partir de identificar el punto de máxima localidad en cada iteración bajo la siguiente lógica:

  1. Primero se especifica la cantidad de clusters requeridos, y que se denotan por la letra \(k\).
  2. Asignación de casos aleatoriamente a los clusters.
  3. Búsqueda de los centroides para cada cluster.
  4. Reasignación de los puntos o casos a los clusters según su posición con respecto del centroide más cercano.
  5. Re ajuste de las posiciones de los centroides dentro de cada cluster.
  6. Repetición de los pasos 4 y 5 hasta que dejan de ocurrir cambios en la asignación de casos a clusters así como de las ubicaciones de los centroides.
  7. Esto arroja, finalmente, el conjunto de cluster con los casos clasificados dentro de cada conglomerado.

2.1. Preparación de los datos

Primero se deben preparar los datos para el análisis, para ello se deben excluir los casos perdidos con na.omit() y, al final, se integra un nuevo objeto que solo contenga a las variables o columnas de interés, con el fin de contar con una matriz de datos limpia.

En este ejercicio se seleccionaron 4 variables a comparar:

  1. Tasa de mortalidad infantil del año 2005.
  2. Tasa de alfabetización del año 2005.
  3. Tasa de asistencia escolar del año 2005.
  4. Ingreso per cápita medido en USD para el año 2005.
datos_idh <- na.omit(datos_idh) # borrado de casos perdidos
sub_idh <- datos_idh %>%
  select(tasa_mortalidad_infantil_2005, #submuestra con las variables de interés solamente.
         tasa_alfabetizacion_2005, 
         tasa_asistencia_escolar_2005,
         usd_ppc_2005)

Posteriormente se debe revisar la distribución de las variables de interés contenidas en la sub muestra, con el fin de identificar las escalas de medición. Esto permitirá identificar si son comparables o si requieren ajustar sus escalas. Para ello es útil revisarlo gráficamente desde un boxplot().

boxplot(sub_idh)

En la gráfica generada se observa que no todas las variables se encuentra en escalas comparables. Pareciera que las 3 primeras variables están en escalas homogéneas entre sí pero difieren demasiado de la variable que mide al ingreso per cápita.

Para resolver este problema en la estructura de datos se transforman las escalas de medición mediante el método de la estandarización, utilizando el comando scale(). Posteriormente se vuelve a solicitar una gráfica de caja para revisar la distribución de las variables.

sub_idh_z <- scale(sub_idh) # standardize variables 
boxplot(sub_idh_z)

Ahora se observa que las variables cuentan con escalas homogéneas que, después, permiten su comparación y, así, agrupamiento posterior.

2.2. Búsqueda del número de clusters

El proceso de determinación del número de clusters pertinentes es un trabajo que requiere de la decisión del analista. En este método, la definición de la cantidad de clusters se apoya en un proceso gráfico en el que el analista debe identificar un “codo” en la curva de distribución de la suma de cuadrados agregados por el número de clusters que se pueden calcular. 

Al identificar dicho “codo” sobre el eje horizontal, se está identificando también el número óptimo de clusters a incluir en el análisis.

wss <- (nrow(sub_idh_z) - 1) * sum(apply(sub_idh_z, 2, var)) # cálculo de la suma de cuadrados de las varianzas
for (i in 2:15) wss[i] <- sum(kmeans(sub_idh_z,
                                     centers = i) $ withinss)
plot(1:15, wss, 
     type = "b", 
     xlab = "Cantidad de Clusters",
     ylab="Suma de cuadrados dentro de grupos") #gráfico para la búsqueda del codo

En el caso de la gráfica generada, el codo de la distribución no es completamente evidente pero se puede asumir que se encuentra entre el cluster 2 y el 3.

2.3. Agregación de los casos en los clusters

Una vez definido la cantidad de clusters que permitirán agregar a los casos del data frame, será importante asignar a cada uno de los casos a cada uno de los cluster. Para ello se utiliza el comando kmeans(), donde se define la cantidad de clusters a implementar. 

Esto se puede guardar como un objeto, del tipo “lista”, y contendrá la asignación de cada caso a cada cluster.

fit1 <- kmeans(sub_idh_z, 3) # ajustar 3 clusters a los casos
sub_idh_z <- data.frame(sub_idh_z, 
                        fit1 $ cluster) #añadir el cluster al data frame
aggregate(sub_idh, #selección de las variables originales
          by = list(sub_idh_z $fit1.cluster), #selección de la columna con clusters `k means`
          FUN = mean) # calcular las medias de las variables agregadas por cada cluster
##   Group.1 tasa_mortalidad_infantil_2005 tasa_alfabetizacion_2005
## 1       1                      32.39373                 68.45623
## 2       2                      22.43107                 85.61483
## 3       3                      14.27039                 93.66232
##   tasa_asistencia_escolar_2005 usd_ppc_2005
## 1                     63.05581     3402.335
## 2                     63.33614     5460.570
## 3                     67.89601    10049.120

Una vez que se ha realizado la asignación previa, es posible caracterizar a cada cluster mediante el cálculo de sus valores descriptivos para cada variable original incorporada. En este caso se generó una tabla con los promedios de cada variable original para cada uno de los clusters calculados. Esto nos permite dar una idea de las diferencias existentes entre los clusters, así como de las características de los casos contenidos en cada agrupamiento.

Finalmente también es posible incorporar una columna, que contiene al cluster de asignación de cada caso, al data frame original par, así, utilizarlo en análisis posteriores.

2.4. Revisión del comportamiento de las variables dentro de cada cluster

Inicialmente se puede revisar la cantidad de casos que fueron asignados a cada cluster, mediante una tabla de frecuencias con el comanto table():

table(sub_idh_z $ fit1.cluster)
## 
##    1    2    3 
##  578 1298  542

Se identificaron que 870 fueron asignados al cluster 1, 662 al cluster 2 y 886 al cluster 3.

A partir de la creación de los clusters, también se puede utilizar esta última variable fit1.cluster para avanzar en el análisis de los casos y las variables orignales.

Por ejemplo, se puede analizar el comportamiento de una variable, como el ingreso per cápita (usd_ppc_2005) desagregado entre los 3 grupos o clusters generados previamente.

sub_idh_z %>%
  ggplot(aes(y = usd_ppc_2005, x = as.factor(fit1.cluster))) +
  geom_boxplot()

A partir de esta distribución, se puede saber que el grupo 2 es el que tiene menor variación del ingreso, pero también es el que registra el menor ingreso promedio. Mientras que el grupo 3 es el caso con mayor variación interna y que, además, registra el mayor promedio de ingreso.

Por otro lado, también es posible conocer la manera en que interactúa el agrupamiento con la distribución de dos variables, simultáneamente. Por ejemplo, si se observa la asociación existente entre el ingreso per cápita (usd_ppc_2005) con respecto a la tasa de mortalidad infantil para el año 2005 (tasa_mortalidad_infantil_2005), y su vinculación con los clusters (k = 3) generados, se obtiene lo siguiente:

sub_idh_z %>%
  ggplot(aes(usd_ppc_2005, tasa_mortalidad_infantil_2005)) +
  geom_point(aes(color = as.factor(fit1.cluster)))

En la gráfica se puede observar que existe una asociación indirecta o negativa entre la tasa de mortalidad infantil y el ingreso per cápita donde, además, el cluster permite identificar variaciones internas en dicha asociación. Se puede identificar que el grupo 2 es el de menor ingreso y mayor mortandad, mientras que en el extremo opuesto se encuentra el grupo 3, de mayor ingreso y menor mortandad.

3. Método jerárquico (hierarchical aggregative method)

El método jerárquico trabaja con el criterio de las distancias existentes entre los casos para, así, ir agrupando la los casos más cercanos entre sí mientras que separa a los casos más distantes entre sí.

El método visual en el que este se apoya consiste en una gráfica de “árbol” o dendograma, y que permite identificar dichas distancias entre los casos.

3.1. Aplicación del método jerárquico en R

En un primer momento se deben calcular las distancias existentes entre los casos para, posteriormente, arribar a la generación del dendograma a partir de estas mediciones.

Para el cálculo de las distancias se utiliza el comando dist(), en donde se especifican que éstas se medirán mediante unidades “euclidianas”, y que se guardará como una matriz de distancias.

Esta matriz de distancias deberá ser tratada, posteriormente, con el comando hclust(), que permite aplicar el método del agrupamiento jerárquico, y en donde se define el criterio para generar los clusters.

En este caso se aplica el criterio de “ward.D”, que permite agregar a los casos más estrechos entre sí, así como separar a los casos más distantes entre sí.

d <- dist(sub_idh_z[, 1:4],  #uso del data fame estandarizado
          method = "euclidean") # genera una matriz de distancias
fit2 <- hclust(d, #aplicación del método jerárquico
              method = "ward.D") #criterio de agregación

3.2. Construcción del dendograma

En gran medida, la meta analítica del análisis jerárquico consiste en la construcción de la gráfica de árbol a partir de las distancias entre los casos.

Para elaborar dicho dendograma se utiliza el comando plot(), y en su interior se ubica al objeto tipo lista generado tras la aplicación del método jerárquico de agrupamiento. También se utiliza el comando rect.hclust(), que permite añadir una capa que consiste en las rectas de color, y que ayudan a identificar los casos contenidos dentro de cada cluster.

plot(fit2) # crear dendograma
rect.hclust(fit2, 
            k = 3, #definición de la cantidad de cluster a mapear en el dendograma
            border = "red") #definición del color de las rectas que separan a los clusters

Esta gráfica denominada dendograma se integra por dos dimensiones. Una horizontal, en la que se ubican a los casos, y están ordenados según las distancias existentes entre cada uno. De manera que los casos posicionados uno al lado del otro indica que son casos muy próximos entre sí, mientras que dos casos muy separados, o inclusive posicionados en los extremos, indica que son muy distantes entre sí.

Las líneas de conexión existentes entre los casos, y que se integran desde la base hacia el tope superior de la gráfica, son las que gráficamente señalan los agrupamientos o clusters posibles a integrar, así como los casos dentro de cada cluster.

El eje vertical de la gráfica, o la altura de las líneas de conexión representa la distancia entre clusters. De manera que dichas líneas de conexión verticales representan clusters mismos. Por lo que líneas con distancias amplias entre ellas, por ejemplo, las alturas más amplias en un mismo nivel de clusters nos proporcionan el número de clusters que mejor representa a nuestros datos.

En el ejemplo que estamos revisando, la mayor distancia o altura entre las líneas de conexión vertical se encuentra en el número de clusters entre 2 y 3.

3.3. Integración de casos a clusters jerárquicos

Una vez que se ha identificado la cantidad de clusters pertinentes a partir del dendograma, es importante avanzar en la revisión visual de la integración de dichos agrupamientos.

Para ello, primero, es importante generar un objeto en el que cada grupo sea asignado a cada cluster, para lo que se utiliza el comando cutree(), a partir del objeto lista previamente elaborado y, además, se define la cantidad de clusters a integrar con k =. Con ello se crea un objeto que, posteriormente, puede se integrado al data frame original.

grupos_j <- cutree(fit2, 
                 k = 3) # se distribuyen los casos a partir de los clusters definidos
sub_idh_z <- data.frame(sub_idh_z, 
                        grupos_j) # se añaden las clasificaciones jerárquicas al data frame original
table(sub_idh_z $ grupos_j)
## 
##   1   2   3 
## 984 801 633

Así se puede identificar que 984 casos fueron agregados en el cluster 1, 801 casos fueron asignados al cluster 2 y 633 casos fueron integrados al cluster 3*.

3.4. Descripción de las variables por categorías jerárquicas

A partir de la creación de los clusters jerárquicos, también se pueden analizar los estadísticos descriptivos para identificar las características internas de cada grupo. Por ejemplo:

aggregate(sub_idh, #selección de variables originales
          by = list(sub_idh_z $ grupos_j), #selección de columna con clusters jerárquicos
          FUN = mean) # calcular las medias de las variables agregadas por cada cluster
##   Group.1 tasa_mortalidad_infantil_2005 tasa_alfabetizacion_2005
## 1       1                      16.85636                 91.86677
## 2       2                      25.60008                 83.55517
## 3       3                      29.19642                 69.72532
##   tasa_asistencia_escolar_2005 usd_ppc_2005
## 1                     66.84040     8171.740
## 2                     59.99688     5246.445
## 3                     65.76266     3566.505

4. Método de agrupamiento basado en modelos (model based clusters).

Este método para calcular la cantidad ideal de clusters asume una variedad de modelos sobre los datos, aplicando estimaciones de máxima verosimilitud, así como modelos basado en criterios de Bayes para, así, identificar el modelo ideal y el número óptimo de clusters.

Este procesamiento de los datos se apoya en el comando Mclust()que está contenido en la librería Mclust(), que calcula diversos modelos de acuerdo modelos BIC, EM, agrupamientos jerárquicos para diversos modelos gaussianos, entre otros más.

Usualmente se suele seleccionar al modelo y cantidad de cluster que refieran al valor más elevado de BIC. Y mediante la “ayuda” de R Studio se pueden consultar los detalles sobre los diversos modelos calculados por la librería (help(mclustModelNames)).

4.1. Construcción de los modelos

La manera de iniciar el análisis consiste en utilizar el comando mclustBIC() sobre el data frame que contiene solo a las variables de interés, por ello se retoma el objeto estandarizado sub_idh_z. A partir de esto se elaboran los diversos modelos que se compararán en la construcción de los clusters.

Posteriormente se solicita que se grafiquen los resultados con plot().

library(mclust)
BIC <- mclustBIC(sub_idh_z[, 1:4]) # selección de las variables estandarizadas
plot(BIC)

Este tipo de análisis se apoya, así como los anteriores, en la revisión de resultados gráficos. El gráfico generado muestra los diversos modelos de ajuste de los datos que se corrió con el comando mclustBIC()

En el plano horizontal de la gráfica BIC se observa el número de componentes o clusters evaluados, mientras que en el eje vertical se muestra el puntaje BIC. Dentro de la gráfica se muestran los puntajes BIC de los distintos modelos evaluados (sus siglas se muestran en el recuadro de texto dentro de la gráfica). 

Y esto permite identificar visualmente el modelo así como la cantidad óptima de clusters a integrar. Esto se define mediante la identificación del modelo que registra el nivel más alto en puntaje BIC, así como su posición sobre el eje horizontal, en la que se define la cantidad de clusters.

4.2. Evaluación de los descriptivos de los modelos

La manera para confirmar esta decisión visual consiste en solicitar un summary(), a partir de calcular los estadísticos de cada uno de los modelos evaluados. Para ello primero se debe aplicar el comando Mclust(..., x = BIC), que permite generar los estadísticos de los modelos, así como la cantidad óptima de clusters. Estos resultados se guardan como objeto para, posteriormente, solicitar la presentación de los resultados.

mod1 <- Mclust(sub_idh, x = BIC)
summary(mod1, parameters = TRUE)
## ---------------------------------------------------- 
## Gaussian finite mixture model fitted by EM algorithm 
## ---------------------------------------------------- 
## 
## Mclust VVV (ellipsoidal, varying volume, shape, and orientation) model with 7
## components: 
## 
##  log-likelihood    n  df       BIC       ICL
##       -44512.97 2418 104 -89836.17 -91641.12
## 
## Clustering table:
##   1   2   3   4   5   6   7 
## 392 501 513 462  28 216 306 
## 
## Mixing probabilities:
##          1          2          3          4          5          6          7 
## 0.16909848 0.21018115 0.19886297 0.17425947 0.01296132 0.09860479 0.13603182 
## 
## Means:
##                                     [,1]       [,2]       [,3]       [,4]
## tasa_mortalidad_infantil_2005   21.15282   29.97466   22.15743   15.72477
## tasa_alfabetizacion_2005        79.83458   69.77325   87.57275   92.40990
## tasa_asistencia_escolar_2005    65.32984   65.10350   62.53432   66.42835
## usd_ppc_2005                  5231.49521 3297.75920 6134.87364 8056.08202
##                                     [,5]        [,6]       [,7]
## tasa_mortalidad_infantil_2005   46.25611    15.03013   28.50974
## tasa_alfabetizacion_2005        66.50453    95.47120   83.49500
## tasa_asistencia_escolar_2005    59.08343    66.76366   60.27991
## usd_ppc_2005                  5411.29702 11218.83401 4551.29616
## 
## Variances:
## [,,1]
##                               tasa_mortalidad_infantil_2005
## tasa_mortalidad_infantil_2005                     11.654741
## tasa_alfabetizacion_2005                         -13.541922
## tasa_asistencia_escolar_2005                      -7.462194
## usd_ppc_2005                                   -2365.829205
##                               tasa_alfabetizacion_2005
## tasa_mortalidad_infantil_2005               -13.541922
## tasa_alfabetizacion_2005                     41.661716
## tasa_asistencia_escolar_2005                  5.288128
## usd_ppc_2005                               4214.901596
##                               tasa_asistencia_escolar_2005  usd_ppc_2005
## tasa_mortalidad_infantil_2005                    -7.462194   -2365.82921
## tasa_alfabetizacion_2005                          5.288128    4214.90160
## tasa_asistencia_escolar_2005                     33.408161     -49.85967
## usd_ppc_2005                                    -49.859674 1843836.26032
## [,,2]
##                               tasa_mortalidad_infantil_2005
## tasa_mortalidad_infantil_2005                      38.89631
## tasa_alfabetizacion_2005                          -32.28219
## tasa_asistencia_escolar_2005                      -12.09764
## usd_ppc_2005                                    -2161.49273
##                               tasa_alfabetizacion_2005
## tasa_mortalidad_infantil_2005                -32.28219
## tasa_alfabetizacion_2005                     104.63028
## tasa_asistencia_escolar_2005                  25.12460
## usd_ppc_2005                                2729.49197
##                               tasa_asistencia_escolar_2005 usd_ppc_2005
## tasa_mortalidad_infantil_2005                    -12.09764   -2161.4927
## tasa_alfabetizacion_2005                          25.12460    2729.4920
## tasa_asistencia_escolar_2005                      42.67613     289.5635
## usd_ppc_2005                                     289.56354  562936.9876
## [,,3]
##                               tasa_mortalidad_infantil_2005
## tasa_mortalidad_infantil_2005                    13.8664019
## tasa_alfabetizacion_2005                         -1.3834079
## tasa_asistencia_escolar_2005                      0.7142234
## usd_ppc_2005                                   -880.8831499
##                               tasa_alfabetizacion_2005
## tasa_mortalidad_infantil_2005               -1.3834079
## tasa_alfabetizacion_2005                    18.0569715
## tasa_asistencia_escolar_2005                 0.0178863
## usd_ppc_2005                              2523.4009039
##                               tasa_asistencia_escolar_2005 usd_ppc_2005
## tasa_mortalidad_infantil_2005                    0.7142234    -880.8831
## tasa_alfabetizacion_2005                         0.0178863    2523.4009
## tasa_asistencia_escolar_2005                    17.0474412   -1141.2293
## usd_ppc_2005                                 -1141.2293496 2138724.3563
## [,,4]
##                               tasa_mortalidad_infantil_2005
## tasa_mortalidad_infantil_2005                     11.192216
## tasa_alfabetizacion_2005                          -3.615167
## tasa_asistencia_escolar_2005                      -5.199677
## usd_ppc_2005                                   -4070.760034
##                               tasa_alfabetizacion_2005
## tasa_mortalidad_infantil_2005                -3.615167
## tasa_alfabetizacion_2005                      7.802156
## tasa_asistencia_escolar_2005                  0.375693
## usd_ppc_2005                               2366.472151
##                               tasa_asistencia_escolar_2005 usd_ppc_2005
## tasa_mortalidad_infantil_2005                    -5.199677    -4070.760
## tasa_alfabetizacion_2005                          0.375693     2366.472
## tasa_asistencia_escolar_2005                     14.231692     1577.270
## usd_ppc_2005                                   1577.270250  5014279.483
## [,,5]
##                               tasa_mortalidad_infantil_2005
## tasa_mortalidad_infantil_2005                     263.90592
## tasa_alfabetizacion_2005                          -90.07249
## tasa_asistencia_escolar_2005                      -95.05444
## usd_ppc_2005                                   -34865.88393
##                               tasa_alfabetizacion_2005
## tasa_mortalidad_infantil_2005                -90.07249
## tasa_alfabetizacion_2005                     304.16991
## tasa_asistencia_escolar_2005                  49.22604
## usd_ppc_2005                               35446.13500
##                               tasa_asistencia_escolar_2005 usd_ppc_2005
## tasa_mortalidad_infantil_2005                    -95.05444    -34865.88
## tasa_alfabetizacion_2005                          49.22604     35446.14
## tasa_asistencia_escolar_2005                      77.78131     10767.90
## usd_ppc_2005                                   10767.89696   8262904.07
## [,,6]
##                               tasa_mortalidad_infantil_2005
## tasa_mortalidad_infantil_2005                     33.847947
## tasa_alfabetizacion_2005                          -7.095209
## tasa_asistencia_escolar_2005                     -17.613196
## usd_ppc_2005                                  -14374.537300
##                               tasa_alfabetizacion_2005
## tasa_mortalidad_infantil_2005                -7.095209
## tasa_alfabetizacion_2005                      2.386335
## tasa_asistencia_escolar_2005                  5.110534
## usd_ppc_2005                               2967.638511
##                               tasa_asistencia_escolar_2005 usd_ppc_2005
## tasa_mortalidad_infantil_2005                   -17.613196   -14374.537
## tasa_alfabetizacion_2005                          5.110534     2967.639
## tasa_asistencia_escolar_2005                     33.073828     3541.202
## usd_ppc_2005                                   3541.202103 19237383.658
## [,,7]
##                               tasa_mortalidad_infantil_2005
## tasa_mortalidad_infantil_2005                     39.224376
## tasa_alfabetizacion_2005                          -9.498475
## tasa_asistencia_escolar_2005                       2.207506
## usd_ppc_2005                                   -1118.456909
##                               tasa_alfabetizacion_2005
## tasa_mortalidad_infantil_2005                -9.498475
## tasa_alfabetizacion_2005                     28.682170
## tasa_asistencia_escolar_2005                 -1.938547
## usd_ppc_2005                               1164.518113
##                               tasa_asistencia_escolar_2005 usd_ppc_2005
## tasa_mortalidad_infantil_2005                     2.207506   -1118.4569
## tasa_alfabetizacion_2005                         -1.938547    1164.5181
## tasa_asistencia_escolar_2005                     20.391738     221.3026
## usd_ppc_2005                                    221.302598  836655.8201

A partir de los resultados presentados es importante observar la tabla de agrupamiento (o clustering table), en donde se define la cantidad óptima de clusters. En el ejercicio realizado con los datos del IDH se identifican 6 clusters como óptimos.

Finalmente, este agrupamiento se puede revisar visualmente en una gráfica de dispersión en la que, a su vez, se solicite que los casos sean representados dentro de cada cluster mediante el comando plot(..., what = "classification").

plot(mod1, what = "classification")

De esta manera se han identificado una mayor cantidad de clusters, y que permite una clasificación más “fina” del comportamiento de los datos en comparación del trabajo mismo que elaboró el PNUD para el caso mexicano, en donde agrupó a los casos en 3 categorías (IDH alto, medio y bajo).

LS0tCmF1dGhvcjogR3VzdGF2byBNYXJ0w61uZXotVmFsZGVzCmRhdGU6ICdgciBmb3JtYXQoU3lzLkRhdGUoKSlgJwp0aXRsZTogQW7DoWxpc2lzIGRlIENvbmdsb21lcmFkb3MgKGNsdXN0ZXJzKQpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgdGhlbWU6IGNvc21vCiAgICB0b2M6IHRydWUKICAgIHRvY19kZXB0aDogNS4wCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICBjb2RlX2ZvbGRpbmc6ICJzaG93IgotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlID0gRkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkKYGBgCgojIDAuIFByZXBhcmFjacOzbiBkZWwgYW1iaWVudGUgZGUgdHJhYmFqbwoKIyMgMC4xLiBJZGlvbWEKRXMgaW1wb3J0YW50ZSBjYW1iaWFyIGVsIGlkaW9tYSBjb24gZWwgcXVlIHNlIGxlIHBpZGUgYWwgYFJgIHF1ZSBkZXRlY3RlIHkgbGVhIGVsIGNvbnRlbmlkbyBkZSBsb3MgYGRhdGEuZnJhbWVgLiBQYXJhIGNhbWJpYXJsbyBhIGVzcGHDsW9sIHNlIHB1ZWRlIHV0aWxpemFyIGVsIHNpZ3VpZW50ZSBjb21hbmRvOgoKYGBge3IgaWRpb21hMX0KU3lzLnNldGxvY2FsZSgiTENfQUxMIiwgImVuX1VTLlVURi04IikKYGBgCgpVbmEgYWx0ZXJuYXRpdmEgZXM6CmBgYHtyIGlkaW9tYTJ9ClN5cy5zZXRlbnYoTEFORyA9ICJzcGEiKQpgYGAKCgoKIyMgMC4yLiBMaWJyZXLDrWFzClNlIHV0aWxpemFyw6FuIGxhcyBsaWJyZXLDrWFzICJyZWFkeGwiLCAidGlkeXZlcnNlIiwgInB2Y2x1c3QiLCAibWNsdXN0IiwgImNsdXN0ZXIiIHkgImZwYyIuXCAKRGViaWRvIGEgcXVlIHNlIHRyYXRhIGRlIHZhcmlhcyBsaWJyZXLDrWFzLCB0YW1iacOpbiBzZSBwdWVkZSB1dGlsaXphciBsYSBsaWJyZXLDrWEgYGVhc3lwYWNrYWdlcygpYCBwYXJhIGhhY2VybG8gc2ltdWx0w6FuZWFtZW50ZS4KClBhcmEgaW5zdGFsYXIgZGljaGFzIGxpYnJlcsOtYXMsIGVuIGNhc28gZGUgbm8gY29udGFyIGNvbiBlbGxhcyBwcmV2aWFtZW50ZSwgc2UgdXRpbGl6YSBlbCBjb21hbmRvIGBpbnN0YWxsLnBhY2thZ2VzKClgIGRlIGxhIHNpZ3VpZW50ZSBtYW5lcmE6CgpgYGAKaW5zdGFsbC5wYWNrYWdlcygicmVhZHhsIikKaW5zdGFsbC5wYWNrYWdlcygidGlkeXZlcnNlIikKaW5zdGFsbC5wYWNrYWdlcygicHZjbHVzdCIpCmluc3RhbGwucGFja2FnZXMoIm1jbHVzdCIpCmluc3RhbGwucGFja2FnZXMoImNsdXN0ZXIiKQppbnN0YWxsLnBhY2thZ2VzKCJmcGMiKQppbnN0YWxsLnBhY2thZ2VzKCJlc2FzeXBhY2thZ2VzIikKYGBgCgpBaG9yYSwgcGFyYSBpbnN0YWxhcmxhcyBzaW11bHTDoW5lYW1lbnRlOgpgYGB7ciBsaWJyZXJpYXN9CmxpYnJhcnkoZWFzeXBhY2thZ2VzKSAjYWN0aXZhY2nDs24gZGUgbGlicmVyw61hCnBhcXVldGVzIDwtIGMoInJlYWR4bCIsICJ0aWR5dmVyc2UiLCAicHZjbHVzdCIsICJtY2x1c3QiLCAiY2x1c3RlciIsICJmcGMiKQpsaWJyYXJpZXMocGFxdWV0ZXMpCmBgYAoKIyMgMC4zIENhcmdhIGRlIGRhdG9zCkVuIGVzdGUgdHV0b3JpYWwgc2UgdXRpbGl6YXLDoSBsYSBiYXNlIGRlIGRhdG9zIGRlbCDDjW5kaWNlIGRlIERlc2Fycm9sbG8gSHVtYW5vIGdlbmVyYWRhIHBvciBlbCBQTlVEIHBhcmEgZWwgY2FzbyBkZSBNw6l4aWNvLCBhZ3JlZ2FkbyBhIG5pdmVsIG11bmljaXBhbCBwYXJhIGxvcyBhw7FvcyAyMDAwIHkgMjAwNS5cClNlIHB1ZWRlIGRlc2NhcmdhciBsYSBiYXNlIGRlIGRhdG9zIHNlIHB1ZWRlIGhhY2VybG8gZGVzZGUgZXN0ZSBbcGVyZmlsIGRlIEdpdEh1Yl0oaHR0cHM6Ly9naXRodWIuY29tL3R1dGFtYW4vaWhkX21waW9fMjAwMF8yMDA1KVwKClVuYSB2ZXogcXVlIHNlIGhhIGRlc2NhcmdhZG8geSBkZXNjb21wcmltaWRvIGxhIG1hdHJpeiBkZSBkYXRvcywgZXN0YSBkZWJlcsOhIGNhcmdhcnNlIGVuIGBSIFN0dWRpb2A6CmBgYHtyIGRhdG9zfQpkYXRvc19pZGggPC0gcmVhZC5jc3YoIn4vRHJvcGJveC9SL2lkaF9tcGlvXzIwMDBfMjAwNS5jc3YiKQpgYGAKCgojIyAwLjQuIFJldmlzacOzbiBkZSBsYSBlc3RydWN0dXJhIGRlbCBgZGF0YS5mcmFtZWAKQW50ZXMgZGUgYXZhbnphciBhIHJlYWxpemFyIGN1YWxxdWllciB0cmFiYWpvIGNvbiBsb3MgZGF0b3MgY29udGVuaWRvcyBlbiBsYSBtYXRyaXosIGVzIGltcG9ydGFudGUgcmV2aXNhciBsYSBlc3RydWN0dXJhIGRlIGxhcyB2YXJpYWJsZXMgZGVsIGRhdGEgZnJhbWUgY29uIGBzdHIoKWAgcGFyYSwgYXPDrSwgaWRlbnRpZmljYXIgbGEgZGltZW5zacOzbiBkZSBsYSBtYXRyaXogZGUgZGF0b3MsIGxhIG1hbmVyYSBlbiBxdWUgZWwgc29mdHdhcmUgaWRlbnRpZmljYSBsb3Mgbm9tYnJlcyBkZSBsYXMgdmFyaWFibGVzLCBhc8OtIGNvbW8gZWwgdGlwbyBkZSB2YXJpYWJsZSBjb24gcXVlIHJlY29ub2NlIGEgY2FkYSB1bmEgeSwgYWRlbcOhcywgdW4gdmlzdGF6byBhIGxvcyB2YWxvcmVzIGRlIGNhZGEgdmFyaWFibGUuCgpgYGB7ciBlc3RydWN0dXJhfQpzdHIoZGF0b3NfaWRoKQpgYGAKCgojIDEuICBQcmVzZW50YWNpw7NuIGFsICpBbsOhbGlzaXMgZGUgQ29uZ2xvbWVyYWRvcyoKCkVsICoqYW7DoWxpc2lzIGRlIGNvbmdsb21lcmFkb3MqKiAobyAqY2x1c3RlciBhbmFseXNpcyopIGVzIHVuYSB0w6ljbmljYSBlc3RhZMOtc3RpY2EgcXVlIHBlcm1pdGUgbGEgY2xhc2lmaWNhY2nDs24gZGUgbG9zIGNhc29zLCBlbiBsYSBxdWUgc2UgaW50ZW50YSBhZ3J1cGFyIGEgbG9zIGNhc29zIGNvbiBjYXJhY3RlcsOtc3RpY2FzIHNpbWlsYXJlcyBlbnRyZSBzw60sIG1pZW50cmFzIHF1ZSBzZSBidXNjYSBxdWUgbG9zIGdydXBvcyBjcmVhZG9zIHNlYW4gZGlmZXJlbnRlcyBlbnRyZSBzw60uIEVzdG8gcGVybWl0ZSBxdWUgb2JqZXRvcyAoYGRhdGEuZnJhbWVzYCBvIG1hdHJpY2VzIGRlIGRhdG9zKSBncmFuZGVzIHNlYW4gImRlc2NvbXB1ZXN0b3MiIGEgc3UgdmV6IGVuIG9iamV0b3MgbcOhcyBwZXF1ZcOxb3MgY3VpZGFuZG8sIGFzw60sIHF1ZSBsb3MgY2Fzb3MgY29udGVuaWRvcyBkZW50cm8gZGUgY2FkYSBncnVwbyBzZWFuIHNlbWVqYW50ZXMgY29uIHJlc3BlY3RvIGRlIG90cm9zIGNhc29zIGNvbnRlbmlkb3MgZW4gb3Ryb3Mgc3ViZ3J1cG9zLlwKCkxhIGludGVncmFjacOzbiBkZSAqY2x1c3RlcnMqIHNlIHB1ZWRlIGVudGVuZGVyIGNvbW8gdW5hIGVzdHJhdGVnaWEgcGFyYSByZXNvbHZlciBwcm9ibGVtYXMgZGUgY2xhc2lmaWNhY2nDs24gZGUgbG9zIGNhc29zLCBhIHBhcnRpciBkZSB0b21hciBlbiBjdWVudGEgdW4gY29uanVudG8gZGUgdmFyaWFibGVzIGNvbXVuZXMuIFNpbiBlbWJhcmdvLCB1bm8gZGUgbG9zIHByb2JsZW1hcyBjb211bmVzIGRlIGVzdGEgdMOpY25pY2EgcmFkaWNhIGVuIGxhIGlkZW50aWZpY2FjacOzbiBkZSBsYSBjYW50aWRhZCBkZSAqY2x1c3RlcnMqIGVuIHF1ZSBzZSBkZWJlbiBpbnRlZ3JhciBsb3MgY2Fzb3MsIHB1ZXMgZXhpc3RlbiBkaXN0aW50YXMgbWFuZXJhcyBwYXJhIGxvZ3JhcmxvIHksIG5vIG5lY2VzYXJpYW1lbnRlLCB0b2RhcyBhcnJpYmFuIGFsIG1pc21vIHJlc3VsdGFkby5cIAoKRW4gZXN0ZSBzZW50aWRvLCBlbCAqYW7DoWxpc2lzIGRlIGNvbmdsb21lcmFkb3MqIG5vIGNvbnNpc3RlIGVuIHVuIGFsZ29yaXRtbyDDum5pY28gcXVlIGFycm9qZSB1biByZXN1bHRhZG8gY29tw7puLCBzaW5vIHF1ZSBlcyB1bmEgZXN0cmF0ZWdpYSBkZSAicmVzb2x1Y2nDs24gZGUgcHJvYmxlbWFzIiBxdWUsIGFkZW3DoXMsIGFicmUgbGEgcHVlcnRhIHBhcmEgbGEgaW50ZXJ2ZW5jacOzbiBkZSBsYSBzdWJqZXRpdmlkYWQgZGVsIGFuYWxpc3RhLlwKCkV4aXN0ZSB1bmEgZ3JhbiBjYW50aWRhZCBkZSBlc3RyYXRlZ2lhcyBwYXJhIGltcGxlbWVudGFyIHVuICoqYW7DoWxpc2lzIGRlIGNvbmdsb21lcmFkb3MqKiwgbGFzIHF1ZSBkZXBlbmRlbiBkZSBsb3MgdGlwb3MgZGUgYWxnb3JpdG1vcyBwYXJhIHJlYWxpemFyIGRpY2hvcyBhZ3J1cGFtaWVudG9zLiBFc3RvcyB0aXBvcyBzZSBwdWVkZW4gY2FyYWN0ZXJpemFyIHBvciBsb3Mgc2lndWllbnRlcyBwcmluY2lwaW9zIG8gY3JpdGVyaW9zIGRlIGFuw6FsaXNpcyBkZSBsb3MgZGF0b3M6CgoxLiBNb2RlbG9zIGRlIGNvbmVjdGl2aWRhZDogRXN0b3MgbW9kZWxvcyBjbGFzaWZpY2FuIGxvcyBjYXNvcyBlbiAqY2x1c3RlcnMqIGJhc2Fkb3MgZW4gbGEgZGlzdGFuY2lhIGV4aXN0ZW50ZSBlbnRyZSBjYWRhIHB1bnRvIG8gY2Fzby4gRWwgcHJpbmNpcGlvIGLDoXNpY28gcXVlIGxvIGd1w61hIGNvbnNpc3RlIGVuIHF1ZSBsb3Mgb2JqZXRvcyBtw6FzIGNlcmNhbm9zIGVudHJlIHPDrSBzZXLDoW4gbcOhcyBzaW1pbGFyZXMgcmVzcGVjdG8gZGUgb3Ryb3Mgb2JqZXRvcyBxdWUgc2UgZW5jdWVudHJlbiBtw6FzIGFsZWphZG9zIGVudHJlIHPDrS4gQSBzdSB2ZXosIGVzdG9zIG1vZGVsb3MgdGllbmVuIGRvcyBhY2VyY2FtaWVudG9zIGEgbGEgY29uc3RydWNjacOzbiBkZSBsb3MgKmNsdXN0ZXJzKjoKICAgIAogICAgKiBFbCBwcmltZXIgYWNlcmNhbWllbnRvIGNvbnNpc3RlIGVuIGRpdmlkaXIgdG9kb3MgbG9zIHB1bnRvcyBlbiBjbHVzdGVyIHksIGRlc3B1w6lzLCBhZ3JlZ2FybG9zIG1pZW50cmFzIHF1ZSBzdXMgZGlzdGFuY2lhcyBzZSBpbmNyZW1lbnRhbi4KICAgICogRWwgc2VndW5kbyBlbmZvcXVlIGNvbnNpc3RlIGVuLCBwcmltZXJvLCBpbnRlZ3JhciBhIHRvZG9zIGxvcyBjYXNvcyBlbiB1biBtaXNtbyBjbHVzdGVyIHksIHBvc3Rlcmlvcm1lbnRlLCBkaXZpZGlybG9zIGVuIG90cm9zIGFncnVwYW1pZW50b3MgYSBtZWRpZGEgcXVlIHN1cyBkaXN0YW5jaWFzIHNlIGluY3JlbWVudGEuCiAgICBBc2ltaXNtbywgbGFzIGRpc3RhbmNpYXMgcHVlZGVuIHZhcmlhciBhIHBhcnRpciBkZWwgYWxnb3JpdG1vIHV0aWxpemFkbyBwYXJhIG1lZGlybGFzLCB5YSBzZWFuLCBwb3IgZWplbXBsbywgbcOpdG9kb3MgZXVjbGlkaWFub3MgbyBtYW5oYXR0YW4gKGNvbW8gbG9zIG3DoXMgY29tdW5lcykuCgoyLiBNb2RlbG9zIGJhc2Fkb3MgZW4gKmNlbnRyb2lkZXMqOiBFc3RvcyBzZSBhcG95YW4gZW4gYWxnb3JpdG1vcyBpdGVyYXRpdm9zIHBhcmEgZ2VuZXJhciBhZ3J1cGFtaWVudG9zIG8gKmNsdXN0ZXJzKi4gTGEgbm9jacOzbiBkZSBzaW1pbGFyaWRhZCBzZSBkZXJpdmEgZGUgbGEgZGlzdGFuY2lhIGRlIGNhZGEgY2FzbyByZXNwZWN0byBkZSB1biAqY2VudHJvaWRlKiBwYXJhIGNhZGEgdW5vIGRlIGxvcyBjbHVzdGVycyBxdWUgc2UgZ2VuZXJhbi4gRXN0b3MgYWxnb3JpdG1vcyByZXF1aWVyZW4sIGRlIGFudGVtYW5vLCBsYSBlc3BlY2lmaWNhY2nDs24gZGVsIG7Dum1lcm8gZGUgKmNsdXN0ZXJzKiBhIGltcGxlbWVudGFyLiBZIGNvbiBjYWRhIGl0ZXJhY2nDs24sIHNlIGNvcnJpZ2UgbGEgcG9zaWNpw7NuIGRlbCAqY2VudHJvaWRlKiBkZSBjYWRhIGNsdXN0ZXIsIGEgbGEgdmV6IHF1ZSBzZSBhanVzdGEgbGEgY2xhc2lmaWNhY2nDs24geSBhc2lnbmFjacOzbiBkZSBjYWRhIGNhc28gYSBsb3MgZGl2ZXJzb3MgKmNsdXN0ZXJzKi4KCjMuIE1vZGVsb3MgYmFzYWRvcyBlbiBkaXN0cmlidWNpb25lczogRXN0b3MgbW9kZWxvcyBkaWZpZXJlbiBkZSBvdHJvcyB0aXBvcyBtw6l0b2RvcyBkZSBhZ3J1cGFtaWVudG8gZGViaWRvIGEgcXVlIGNvbnNpZGVyYW4gbGEgcHJvYmFiaWxpZGFkIGRlIHBlcnRlbmVuY2lhIGRlIGNhZGEgY2FzbyBhIGNhZGEgY2x1c3RlciwgZW4gbHVnYXIgZGUgdG9tYXIgZW4gY29uc2lkZXJhY2nDs24gbGFzIGRpc3RhbmNpYXMgZW50cmUgZGF0b3MuIEVzdG9zIG1vZGVsb3Mgc3VlbGVuIHN1ZnJpciBkZSBzb2JyZS1hanVzdGUuCgo0LiBNb2RlbG9zIGRlIGRlbnNpZGFkOiBFc3RvcyBtb2RlbG9zIGNvbnNpZGVyYW4gbGEgZGVuc2lkYWQgZGUgbG9zIHB1bnRvcyBvIGNhc29zIGVuIGRpZmVyZW50ZXMgcGFydGVzIGRlbCBlc3BhY2lvIHBhcmEsIGFzw60sIGNyZWFyICpjbHVzdGVycyogZW4gbG9zIHN1YmVzcGFjaW9zIGNvbiBkZW5zaWRhZGVzIHNpbWlsYXJlcy4gU3VlbGVuIGFpc2xhciB2YXJpb3Mgc3ViZXNwYWNpb3MgYmFzYWRvIGVuIGxhIGRlbnNpZGFkIGRlIGNhZGEgcHVudG8gZGUgaW5mb3JtYWNpw7NuIHByZXNlbnRlIHkgYXNpZ25hbiBhIGxvcyBjYXNvcyBlbnRyZSBsb3MgY2x1c3RlcnMgc2VwYXJhZG9zLgoKRW4gZXN0ZSB0dXRvcmlhbCBzZSByZXZpc2Fyw6FuIDMgdMOpY25pY2FzIHBhcmEgZWwgYW7DoWxpc2lzIGRlIGNsdXN0ZXJzLCBxdWUgaW50ZWdyYW4gYSBhbGd1bm9zIGRlIGVzdG9zIHRpcG9zIGRlIGNyaXRlcmlvcyBkZSBjb25nbG9tZXJhY2nDs246IAoKMS4gICAgTcOpdG9kbyBkZSBrLW1lZGlhcyhwYXJ0aXRpb25pbmcgay1tZWFucykuCjIuICAgIE3DqXRvZG8gamVyw6FycXVpY28gKGhpZXJhcmNoaWNhbCBhZ2dsb21lcmF0aXZlKS4KMy4gICAgTcOpdG9kb3MgYmFzYWRvcyBlbiBtb2RlbG9zLgoKCiMgMi4gTcOpdG9kbyBrLW1lZGlhcyAoay1tZWFucykKRWwgbcOpdG9kbyBkZSBwYXJ0aWNpw7NuIG8gay1tZWRpYXMgKCpwYXJ0aXRpb25pbmcgay1tZWFucyopIHRyYWJhamEgY29uIGxhIGzDs2dpY2EgZGUgbG9zIG1vZGVsb3MgaXRlcmF0aXZvcyBjb24gKmNlbnRyb2lkZXMqLCB5IHRyYWJhamEgYSBwYXJ0aXIgZGUgaWRlbnRpZmljYXIgZWwgcHVudG8gZGUgbcOheGltYSBsb2NhbGlkYWQgZW4gY2FkYSBpdGVyYWNpw7NuIGJham8gbGEgc2lndWllbnRlIGzDs2dpY2E6CgoxLiBQcmltZXJvIHNlIGVzcGVjaWZpY2EgbGEgY2FudGlkYWQgZGUgKmNsdXN0ZXJzKiByZXF1ZXJpZG9zLCB5IHF1ZSBzZSBkZW5vdGFuIHBvciBsYSBsZXRyYSAkayQuCjIuIEFzaWduYWNpw7NuIGRlIGNhc29zIGFsZWF0b3JpYW1lbnRlIGEgbG9zICpjbHVzdGVycyouCjMuIELDunNxdWVkYSBkZSBsb3MgKmNlbnRyb2lkZXMqIHBhcmEgY2FkYSAqY2x1c3RlciouCjQuIFJlYXNpZ25hY2nDs24gZGUgbG9zIHB1bnRvcyBvIGNhc29zIGEgbG9zICpjbHVzdGVycyogc2Vnw7puIHN1IHBvc2ljacOzbiBjb24gcmVzcGVjdG8gZGVsICpjZW50cm9pZGUqIG3DoXMgY2VyY2Fuby4KNS4gUmUgYWp1c3RlIGRlIGxhcyBwb3NpY2lvbmVzIGRlIGxvcyAqY2VudHJvaWRlcyogZGVudHJvIGRlIGNhZGEgKmNsdXN0ZXIqLgo2LiBSZXBldGljacOzbiBkZSBsb3MgcGFzb3MgNCB5IDUgaGFzdGEgcXVlIGRlamFuIGRlIG9jdXJyaXIgY2FtYmlvcyBlbiBsYSBhc2lnbmFjacOzbiBkZSBjYXNvcyBhICpjbHVzdGVycyogYXPDrSBjb21vIGRlIGxhcyB1YmljYWNpb25lcyBkZSBsb3MgKmNlbnRyb2lkZXMqLgo3LiBFc3RvIGFycm9qYSwgZmluYWxtZW50ZSwgZWwgY29uanVudG8gZGUgKmNsdXN0ZXIqIGNvbiBsb3MgY2Fzb3MgY2xhc2lmaWNhZG9zIGRlbnRybyBkZSBjYWRhIGNvbmdsb21lcmFkby4KCiMjIDIuMS4gUHJlcGFyYWNpw7NuIGRlIGxvcyBkYXRvcwpQcmltZXJvIHNlIGRlYmVuIHByZXBhcmFyIGxvcyBkYXRvcyBwYXJhIGVsIGFuw6FsaXNpcywgcGFyYSBlbGxvIHNlIGRlYmVuIGV4Y2x1aXIgbG9zIGNhc29zIHBlcmRpZG9zIGNvbiBgbmEub21pdCgpYCB5LCBhbCBmaW5hbCwgc2UgaW50ZWdyYSB1biBudWV2byBvYmpldG8gcXVlIHNvbG8gY29udGVuZ2EgYSBsYXMgdmFyaWFibGVzIG8gY29sdW1uYXMgZGUgaW50ZXLDqXMsIGNvbiBlbCBmaW4gZGUgY29udGFyIGNvbiB1bmEgbWF0cml6IGRlIGRhdG9zICpsaW1waWEqLgoKRW4gZXN0ZSBlamVyY2ljaW8gc2Ugc2VsZWNjaW9uYXJvbiA0IHZhcmlhYmxlcyBhIGNvbXBhcmFyOgoKMS4gVGFzYSBkZSBtb3J0YWxpZGFkIGluZmFudGlsIGRlbCBhw7FvIDIwMDUuCjIuIFRhc2EgZGUgYWxmYWJldGl6YWNpw7NuIGRlbCBhw7FvIDIwMDUuCjMuIFRhc2EgZGUgYXNpc3RlbmNpYSBlc2NvbGFyIGRlbCBhw7FvIDIwMDUuCjQuIEluZ3Jlc28gcGVyIGPDoXBpdGEgbWVkaWRvIGVuIFVTRCBwYXJhIGVsIGHDsW8gMjAwNS4KCmBgYHtyIHN1Yl9tdWVzdHJhfQpkYXRvc19pZGggPC0gbmEub21pdChkYXRvc19pZGgpICMgYm9ycmFkbyBkZSBjYXNvcyBwZXJkaWRvcwpzdWJfaWRoIDwtIGRhdG9zX2lkaCAlPiUKICBzZWxlY3QodGFzYV9tb3J0YWxpZGFkX2luZmFudGlsXzIwMDUsICNzdWJtdWVzdHJhIGNvbiBsYXMgdmFyaWFibGVzIGRlIGludGVyw6lzIHNvbGFtZW50ZS4KICAgICAgICAgdGFzYV9hbGZhYmV0aXphY2lvbl8yMDA1LCAKICAgICAgICAgdGFzYV9hc2lzdGVuY2lhX2VzY29sYXJfMjAwNSwKICAgICAgICAgdXNkX3BwY18yMDA1KQpgYGAKClBvc3Rlcmlvcm1lbnRlIHNlIGRlYmUgcmV2aXNhciBsYSBkaXN0cmlidWNpw7NuIGRlIGxhcyB2YXJpYWJsZXMgZGUgaW50ZXLDqXMgY29udGVuaWRhcyBlbiBsYSBzdWIgbXVlc3RyYSwgY29uIGVsIGZpbiBkZSBpZGVudGlmaWNhciBsYXMgZXNjYWxhcyBkZSBtZWRpY2nDs24uIEVzdG8gcGVybWl0aXLDoSBpZGVudGlmaWNhciBzaSBzb24gY29tcGFyYWJsZXMgbyBzaSByZXF1aWVyZW4gYWp1c3RhciBzdXMgZXNjYWxhcy4gUGFyYSBlbGxvIGVzIMO6dGlsIHJldmlzYXJsbyBncsOhZmljYW1lbnRlIGRlc2RlIHVuIGBib3hwbG90KClgLgoKYGBge3IgYm94cGxvdH0KYm94cGxvdChzdWJfaWRoKQpgYGAKCkVuIGxhIGdyw6FmaWNhIGdlbmVyYWRhIHNlIG9ic2VydmEgcXVlIG5vIHRvZGFzIGxhcyB2YXJpYWJsZXMgc2UgZW5jdWVudHJhIGVuIGVzY2FsYXMgY29tcGFyYWJsZXMuIFBhcmVjaWVyYSBxdWUgbGFzIDMgcHJpbWVyYXMgdmFyaWFibGVzIGVzdMOhbiBlbiBlc2NhbGFzIGhvbW9nw6luZWFzIGVudHJlIHPDrSBwZXJvIGRpZmllcmVuIGRlbWFzaWFkbyBkZSBsYSB2YXJpYWJsZSBxdWUgbWlkZSBhbCBpbmdyZXNvICpwZXIgY8OhcGl0YSouXAoKUGFyYSByZXNvbHZlciBlc3RlIHByb2JsZW1hIGVuIGxhIGVzdHJ1Y3R1cmEgZGUgZGF0b3Mgc2UgdHJhbnNmb3JtYW4gbGFzIGVzY2FsYXMgZGUgbWVkaWNpw7NuIG1lZGlhbnRlIGVsIG3DqXRvZG8gZGUgbGEgZXN0YW5kYXJpemFjacOzbiwgdXRpbGl6YW5kbyBlbCBjb21hbmRvIGBzY2FsZSgpYC4gUG9zdGVyaW9ybWVudGUgc2UgdnVlbHZlIGEgc29saWNpdGFyIHVuYSBncsOhZmljYSBkZSBjYWphIHBhcmEgcmV2aXNhciBsYSBkaXN0cmlidWNpw7NuIGRlIGxhcyB2YXJpYWJsZXMuCgpgYGB7ciBlc3RhbmRhcml6YWNpb259CnN1Yl9pZGhfeiA8LSBzY2FsZShzdWJfaWRoKSAjIHN0YW5kYXJkaXplIHZhcmlhYmxlcyAKYm94cGxvdChzdWJfaWRoX3opCmBgYAoKQWhvcmEgc2Ugb2JzZXJ2YSBxdWUgbGFzIHZhcmlhYmxlcyBjdWVudGFuIGNvbiBlc2NhbGFzIGhvbW9nw6luZWFzIHF1ZSwgZGVzcHXDqXMsIHBlcm1pdGVuIHN1IGNvbXBhcmFjacOzbiB5LCBhc8OtLCBhZ3J1cGFtaWVudG8gcG9zdGVyaW9yLgoKCiMjIDIuMi4gQsO6c3F1ZWRhIGRlbCBuw7ptZXJvIGRlIGNsdXN0ZXJzCgpFbCBwcm9jZXNvIGRlIGRldGVybWluYWNpw7NuIGRlbCBuw7ptZXJvIGRlIGNsdXN0ZXJzIHBlcnRpbmVudGVzIGVzIHVuIHRyYWJham8gcXVlIHJlcXVpZXJlIGRlIGxhIGRlY2lzacOzbiBkZWwgYW5hbGlzdGEuIEVuIGVzdGUgbcOpdG9kbywgbGEgZGVmaW5pY2nDs24gZGUgbGEgY2FudGlkYWQgZGUgY2x1c3RlcnMgc2UgYXBveWEgZW4gdW4gcHJvY2VzbyBncsOhZmljbyBlbiBlbCBxdWUgZWwgYW5hbGlzdGEgZGViZSBpZGVudGlmaWNhciB1biAiKmNvZG8qIiBlbiBsYSBjdXJ2YSBkZSBkaXN0cmlidWNpw7NuIGRlIGxhIHN1bWEgZGUgY3VhZHJhZG9zIGFncmVnYWRvcyBwb3IgZWwgbsO6bWVybyBkZSBjbHVzdGVycyBxdWUgc2UgcHVlZGVuIGNhbGN1bGFyLlwgCgpBbCBpZGVudGlmaWNhciBkaWNobyAiKmNvZG8qIiBzb2JyZSBlbCBlamUgaG9yaXpvbnRhbCwgc2UgZXN0w6EgaWRlbnRpZmljYW5kbyB0YW1iacOpbiBlbCBuw7ptZXJvIMOzcHRpbW8gZGUgY2x1c3RlcnMgYSBpbmNsdWlyIGVuIGVsIGFuw6FsaXNpcy4KCmBgYHtyIGNvZG99CndzcyA8LSAobnJvdyhzdWJfaWRoX3opIC0gMSkgKiBzdW0oYXBwbHkoc3ViX2lkaF96LCAyLCB2YXIpKSAjIGPDoWxjdWxvIGRlIGxhIHN1bWEgZGUgY3VhZHJhZG9zIGRlIGxhcyB2YXJpYW56YXMKZm9yIChpIGluIDI6MTUpIHdzc1tpXSA8LSBzdW0oa21lYW5zKHN1Yl9pZGhfeiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNlbnRlcnMgPSBpKSAkIHdpdGhpbnNzKQpwbG90KDE6MTUsIHdzcywgCiAgICAgdHlwZSA9ICJiIiwgCiAgICAgeGxhYiA9ICJDYW50aWRhZCBkZSBDbHVzdGVycyIsCiAgICAgeWxhYj0iU3VtYSBkZSBjdWFkcmFkb3MgZGVudHJvIGRlIGdydXBvcyIpICNncsOhZmljbyBwYXJhIGxhIGLDunNxdWVkYSBkZWwgY29kbwpgYGAKCkVuIGVsIGNhc28gZGUgbGEgZ3LDoWZpY2EgZ2VuZXJhZGEsIGVsIGNvZG8gZGUgbGEgZGlzdHJpYnVjacOzbiBubyBlcyBjb21wbGV0YW1lbnRlIGV2aWRlbnRlIHBlcm8gc2UgcHVlZGUgYXN1bWlyIHF1ZSBzZSBlbmN1ZW50cmEgZW50cmUgZWwgY2x1c3RlciAyIHkgZWwgMy5cCgoKIyMgMi4zLiBBZ3JlZ2FjacOzbiBkZSBsb3MgY2Fzb3MgZW4gbG9zIGNsdXN0ZXJzClVuYSB2ZXogZGVmaW5pZG8gbGEgY2FudGlkYWQgZGUgY2x1c3RlcnMgcXVlIHBlcm1pdGlyw6FuIGFncmVnYXIgYSBsb3MgY2Fzb3MgZGVsIGRhdGEgZnJhbWUsIHNlcsOhIGltcG9ydGFudGUgYXNpZ25hciBhIGNhZGEgdW5vIGRlIGxvcyBjYXNvcyBhIGNhZGEgdW5vIGRlIGxvcyBjbHVzdGVyLiBQYXJhIGVsbG8gc2UgdXRpbGl6YSBlbCBjb21hbmRvIGBrbWVhbnMoKWAsIGRvbmRlIHNlIGRlZmluZSBsYSBjYW50aWRhZCBkZSBjbHVzdGVycyBhIGltcGxlbWVudGFyLlwgCgpFc3RvIHNlIHB1ZWRlIGd1YXJkYXIgY29tbyB1biBvYmpldG8sIGRlbCB0aXBvICJsaXN0YSIsIHkgY29udGVuZHLDoSBsYSBhc2lnbmFjacOzbiBkZSBjYWRhIGNhc28gYSBjYWRhIGNsdXN0ZXIuCgpgYGB7cn0KZml0MSA8LSBrbWVhbnMoc3ViX2lkaF96LCAzKSAjIGFqdXN0YXIgMyBjbHVzdGVycyBhIGxvcyBjYXNvcwpzdWJfaWRoX3ogPC0gZGF0YS5mcmFtZShzdWJfaWRoX3osIAogICAgICAgICAgICAgICAgICAgICAgICBmaXQxICQgY2x1c3RlcikgI2HDsWFkaXIgZWwgY2x1c3RlciBhbCBkYXRhIGZyYW1lCmFnZ3JlZ2F0ZShzdWJfaWRoLCAjc2VsZWNjacOzbiBkZSBsYXMgdmFyaWFibGVzIG9yaWdpbmFsZXMKICAgICAgICAgIGJ5ID0gbGlzdChzdWJfaWRoX3ogJGZpdDEuY2x1c3RlciksICNzZWxlY2Npw7NuIGRlIGxhIGNvbHVtbmEgY29uIGNsdXN0ZXJzIGBrIG1lYW5zYAogICAgICAgICAgRlVOID0gbWVhbikgIyBjYWxjdWxhciBsYXMgbWVkaWFzIGRlIGxhcyB2YXJpYWJsZXMgYWdyZWdhZGFzIHBvciBjYWRhIGNsdXN0ZXIKCmBgYAoKVW5hIHZleiBxdWUgc2UgaGEgcmVhbGl6YWRvIGxhIGFzaWduYWNpw7NuIHByZXZpYSwgZXMgcG9zaWJsZSBjYXJhY3Rlcml6YXIgYSBjYWRhIGNsdXN0ZXIgbWVkaWFudGUgZWwgY8OhbGN1bG8gZGUgc3VzIHZhbG9yZXMgZGVzY3JpcHRpdm9zIHBhcmEgY2FkYSB2YXJpYWJsZSBvcmlnaW5hbCBpbmNvcnBvcmFkYS4gRW4gZXN0ZSBjYXNvIHNlIGdlbmVyw7MgdW5hIHRhYmxhIGNvbiBsb3MgcHJvbWVkaW9zIGRlIGNhZGEgdmFyaWFibGUgb3JpZ2luYWwgcGFyYSBjYWRhIHVubyBkZSBsb3MgY2x1c3RlcnMgY2FsY3VsYWRvcy4gRXN0byBub3MgcGVybWl0ZSBkYXIgdW5hIGlkZWEgZGUgbGFzIGRpZmVyZW5jaWFzIGV4aXN0ZW50ZXMgZW50cmUgbG9zIGNsdXN0ZXJzLCBhc8OtIGNvbW8gZGUgbGFzIGNhcmFjdGVyw61zdGljYXMgZGUgbG9zIGNhc29zIGNvbnRlbmlkb3MgZW4gY2FkYSBhZ3J1cGFtaWVudG8uXAoKRmluYWxtZW50ZSB0YW1iacOpbiBlcyBwb3NpYmxlIGluY29ycG9yYXIgdW5hIGNvbHVtbmEsIHF1ZSBjb250aWVuZSBhbCBjbHVzdGVyIGRlIGFzaWduYWNpw7NuIGRlIGNhZGEgY2FzbywgYWwgZGF0YSBmcmFtZSBvcmlnaW5hbCBwYXIsIGFzw60sIHV0aWxpemFybG8gZW4gYW7DoWxpc2lzIHBvc3RlcmlvcmVzLlwKCgojIyAyLjQuIFJldmlzacOzbiBkZWwgY29tcG9ydGFtaWVudG8gZGUgbGFzIHZhcmlhYmxlcyBkZW50cm8gZGUgY2FkYSBjbHVzdGVyCkluaWNpYWxtZW50ZSBzZSBwdWVkZSByZXZpc2FyIGxhIGNhbnRpZGFkIGRlIGNhc29zIHF1ZSBmdWVyb24gYXNpZ25hZG9zIGEgY2FkYSBjbHVzdGVyLCBtZWRpYW50ZSB1bmEgdGFibGEgZGUgZnJlY3VlbmNpYXMgY29uIGVsIGNvbWFudG8gYHRhYmxlKClgOgoKYGBge3IgZnJlY19jbHVzdDF9CnRhYmxlKHN1Yl9pZGhfeiAkIGZpdDEuY2x1c3RlcikKYGBgCgpTZSBpZGVudGlmaWNhcm9uIHF1ZSA4NzAgZnVlcm9uIGFzaWduYWRvcyBhbCBjbHVzdGVyIDEsIDY2MiBhbCBjbHVzdGVyIDIgeSA4ODYgYWwgY2x1c3RlciAzLlwKCkEgcGFydGlyIGRlIGxhIGNyZWFjacOzbiBkZSBsb3MgY2x1c3RlcnMsIHRhbWJpw6luIHNlIHB1ZWRlIHV0aWxpemFyIGVzdGEgw7psdGltYSB2YXJpYWJsZSBgZml0MS5jbHVzdGVyYCBwYXJhIGF2YW56YXIgZW4gZWwgYW7DoWxpc2lzIGRlIGxvcyBjYXNvcyB5IGxhcyB2YXJpYWJsZXMgb3JpZ25hbGVzLlwKClBvciBlamVtcGxvLCBzZSBwdWVkZSBhbmFsaXphciBlbCBjb21wb3J0YW1pZW50byBkZSB1bmEgdmFyaWFibGUsIGNvbW8gZWwgaW5ncmVzbyAqcGVyIGPDoXBpdGEqIChgdXNkX3BwY18yMDA1YCkgZGVzYWdyZWdhZG8gZW50cmUgbG9zIDMgZ3J1cG9zIG8gY2x1c3RlcnMgZ2VuZXJhZG9zIHByZXZpYW1lbnRlLgoKYGBge3IgaW5ncmVzb19jbHVzdGVyfQpzdWJfaWRoX3ogJT4lCiAgZ2dwbG90KGFlcyh5ID0gdXNkX3BwY18yMDA1LCB4ID0gYXMuZmFjdG9yKGZpdDEuY2x1c3RlcikpKSArCiAgZ2VvbV9ib3hwbG90KCkKYGBgCgpBIHBhcnRpciBkZSBlc3RhIGRpc3RyaWJ1Y2nDs24sIHNlIHB1ZWRlIHNhYmVyIHF1ZSBlbCBncnVwbyAyIGVzIGVsIHF1ZSB0aWVuZSBtZW5vciB2YXJpYWNpw7NuIGRlbCBpbmdyZXNvLCBwZXJvIHRhbWJpw6luIGVzIGVsIHF1ZSByZWdpc3RyYSBlbCBtZW5vciBpbmdyZXNvIHByb21lZGlvLiBNaWVudHJhcyBxdWUgZWwgZ3J1cG8gMyBlcyBlbCBjYXNvIGNvbiBtYXlvciB2YXJpYWNpw7NuIGludGVybmEgeSBxdWUsIGFkZW3DoXMsIHJlZ2lzdHJhIGVsIG1heW9yIHByb21lZGlvIGRlIGluZ3Jlc28uXAoKUG9yIG90cm8gbGFkbywgdGFtYmnDqW4gZXMgcG9zaWJsZSBjb25vY2VyIGxhIG1hbmVyYSBlbiBxdWUgaW50ZXJhY3TDumEgZWwgYWdydXBhbWllbnRvIGNvbiBsYSBkaXN0cmlidWNpw7NuIGRlIGRvcyB2YXJpYWJsZXMsIHNpbXVsdMOhbmVhbWVudGUuIFBvciBlamVtcGxvLCBzaSBzZSBvYnNlcnZhIGxhIGFzb2NpYWNpw7NuIGV4aXN0ZW50ZSBlbnRyZSBlbCBpbmdyZXNvICpwZXIgY8OhcGl0YSogKGB1c2RfcHBjXzIwMDVgKSBjb24gcmVzcGVjdG8gYSBsYSB0YXNhIGRlIG1vcnRhbGlkYWQgaW5mYW50aWwgcGFyYSBlbCBhw7FvIDIwMDUgKGB0YXNhX21vcnRhbGlkYWRfaW5mYW50aWxfMjAwNWApLCB5IHN1IHZpbmN1bGFjacOzbiBjb24gbG9zIGNsdXN0ZXJzIChrID0gMykgZ2VuZXJhZG9zLCBzZSBvYnRpZW5lIGxvIHNpZ3VpZW50ZToKCmBgYHtyIGRpc3BlcnNpb25fY2x1c3RlcnN9CnN1Yl9pZGhfeiAlPiUKICBnZ3Bsb3QoYWVzKHVzZF9wcGNfMjAwNSwgdGFzYV9tb3J0YWxpZGFkX2luZmFudGlsXzIwMDUpKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBhcy5mYWN0b3IoZml0MS5jbHVzdGVyKSkpCmBgYAoKRW4gbGEgZ3LDoWZpY2Egc2UgcHVlZGUgb2JzZXJ2YXIgcXVlIGV4aXN0ZSB1bmEgYXNvY2lhY2nDs24gaW5kaXJlY3RhIG8gbmVnYXRpdmEgZW50cmUgbGEgdGFzYSBkZSBtb3J0YWxpZGFkIGluZmFudGlsIHkgZWwgaW5ncmVzbyAqcGVyIGPDoXBpdGEqIGRvbmRlLCBhZGVtw6FzLCBlbCBjbHVzdGVyIHBlcm1pdGUgaWRlbnRpZmljYXIgdmFyaWFjaW9uZXMgaW50ZXJuYXMgZW4gZGljaGEgYXNvY2lhY2nDs24uIFNlIHB1ZWRlIGlkZW50aWZpY2FyIHF1ZSBlbCBncnVwbyAyIGVzIGVsIGRlIG1lbm9yIGluZ3Jlc28geSBtYXlvciBtb3J0YW5kYWQsIG1pZW50cmFzIHF1ZSBlbiBlbCBleHRyZW1vIG9wdWVzdG8gc2UgZW5jdWVudHJhIGVsIGdydXBvIDMsIGRlIG1heW9yIGluZ3Jlc28geSBtZW5vciBtb3J0YW5kYWQuXAoKCiMgMy4gTcOpdG9kbyBqZXLDoXJxdWljbyAoKmhpZXJhcmNoaWNhbCBhZ2dyZWdhdGl2ZSBtZXRob2QqKQoKRWwgbcOpdG9kbyBqZXLDoXJxdWljbyB0cmFiYWphIGNvbiBlbCBjcml0ZXJpbyBkZSBsYXMgZGlzdGFuY2lhcyBleGlzdGVudGVzIGVudHJlIGxvcyBjYXNvcyBwYXJhLCBhc8OtLCBpciBhZ3J1cGFuZG8gbGEgbG9zIGNhc29zIG3DoXMgY2VyY2Fub3MgZW50cmUgc8OtIG1pZW50cmFzIHF1ZSBzZXBhcmEgYSBsb3MgY2Fzb3MgbcOhcyBkaXN0YW50ZXMgZW50cmUgc8OtLlwKCkVsIG3DqXRvZG8gdmlzdWFsIGVuIGVsIHF1ZSBlc3RlIHNlIGFwb3lhIGNvbnNpc3RlIGVuIHVuYSBncsOhZmljYSBkZSAiw6FyYm9sIiBvICpkZW5kb2dyYW1hKiwgeSBxdWUgcGVybWl0ZSBpZGVudGlmaWNhciBkaWNoYXMgZGlzdGFuY2lhcyBlbnRyZSBsb3MgY2Fzb3MuXAoKCiMjIDMuMS4gQXBsaWNhY2nDs24gZGVsIG3DqXRvZG8gamVyw6FycXVpY28gZW4gUgpFbiB1biBwcmltZXIgbW9tZW50byBzZSBkZWJlbiBjYWxjdWxhciBsYXMgZGlzdGFuY2lhcyBleGlzdGVudGVzIGVudHJlIGxvcyBjYXNvcyBwYXJhLCBwb3N0ZXJpb3JtZW50ZSwgYXJyaWJhciBhIGxhIGdlbmVyYWNpw7NuIGRlbCAqZGVuZG9ncmFtYSogYSBwYXJ0aXIgZGUgZXN0YXMgbWVkaWNpb25lcy4KClBhcmEgZWwgY8OhbGN1bG8gZGUgbGFzIGRpc3RhbmNpYXMgc2UgdXRpbGl6YSBlbCBjb21hbmRvIGBkaXN0KClgLCBlbiBkb25kZSBzZSBlc3BlY2lmaWNhbiBxdWUgw6lzdGFzIHNlIG1lZGlyw6FuIG1lZGlhbnRlIHVuaWRhZGVzICJldWNsaWRpYW5hcyIsIHkgcXVlIHNlIGd1YXJkYXLDoSBjb21vIHVuYSBtYXRyaXogZGUgZGlzdGFuY2lhcy5cCgpFc3RhIG1hdHJpeiBkZSBkaXN0YW5jaWFzIGRlYmVyw6Egc2VyIHRyYXRhZGEsIHBvc3Rlcmlvcm1lbnRlLCBjb24gZWwgY29tYW5kbyBgaGNsdXN0KClgLCBxdWUgcGVybWl0ZSBhcGxpY2FyIGVsIG3DqXRvZG8gZGVsIGFncnVwYW1pZW50byBqZXLDoXJxdWljbywgeSBlbiBkb25kZSBzZSBkZWZpbmUgZWwgY3JpdGVyaW8gcGFyYSBnZW5lcmFyIGxvcyAqY2x1c3RlcnMuKlwKCkVuIGVzdGUgY2FzbyBzZSBhcGxpY2EgZWwgY3JpdGVyaW8gZGUgIndhcmQuRCIsIHF1ZSBwZXJtaXRlIGFncmVnYXIgYSBsb3MgY2Fzb3MgbcOhcyBlc3RyZWNob3MgZW50cmUgc8OtLCBhc8OtIGNvbW8gc2VwYXJhciBhIGxvcyBjYXNvcyBtw6FzIGRpc3RhbnRlcyBlbnRyZSBzw60uCgpgYGB7cn0KZCA8LSBkaXN0KHN1Yl9pZGhfelssIDE6NF0sICAjdXNvIGRlbCBkYXRhIGZhbWUgZXN0YW5kYXJpemFkbwogICAgICAgICAgbWV0aG9kID0gImV1Y2xpZGVhbiIpICMgZ2VuZXJhIHVuYSBtYXRyaXogZGUgZGlzdGFuY2lhcwpmaXQyIDwtIGhjbHVzdChkLCAjYXBsaWNhY2nDs24gZGVsIG3DqXRvZG8gamVyw6FycXVpY28KICAgICAgICAgICAgICBtZXRob2QgPSAid2FyZC5EIikgI2NyaXRlcmlvIGRlIGFncmVnYWNpw7NuCmBgYAoKCiMjIDMuMi4gQ29uc3RydWNjacOzbiBkZWwgZGVuZG9ncmFtYQpFbiBncmFuIG1lZGlkYSwgbGEgbWV0YSBhbmFsw610aWNhIGRlbCBhbsOhbGlzaXMgamVyw6FycXVpY28gY29uc2lzdGUgZW4gbGEgY29uc3RydWNjacOzbiBkZSBsYSBncsOhZmljYSBkZSDDoXJib2wgYSBwYXJ0aXIgZGUgbGFzIGRpc3RhbmNpYXMgZW50cmUgbG9zIGNhc29zLlwKClBhcmEgZWxhYm9yYXIgZGljaG8gKmRlbmRvZ3JhbWEqIHNlIHV0aWxpemEgZWwgY29tYW5kbyBgcGxvdCgpYCwgeSBlbiBzdSBpbnRlcmlvciBzZSB1YmljYSBhbCBvYmpldG8gdGlwbyBsaXN0YSBnZW5lcmFkbyB0cmFzIGxhIGFwbGljYWNpw7NuIGRlbCBtw6l0b2RvIGplcsOhcnF1aWNvIGRlIGFncnVwYW1pZW50by4gVGFtYmnDqW4gc2UgdXRpbGl6YSBlbCBjb21hbmRvIGByZWN0LmhjbHVzdCgpYCwgcXVlIHBlcm1pdGUgYcOxYWRpciB1bmEgY2FwYSBxdWUgY29uc2lzdGUgZW4gbGFzIHJlY3RhcyBkZSBjb2xvciwgeSBxdWUgYXl1ZGFuIGEgaWRlbnRpZmljYXIgbG9zIGNhc29zIGNvbnRlbmlkb3MgZGVudHJvIGRlIGNhZGEgY2x1c3Rlci4KCmBgYHtyIGRlbmRvZ3JhbWF9CnBsb3QoZml0MikgIyBjcmVhciBkZW5kb2dyYW1hCnJlY3QuaGNsdXN0KGZpdDIsIAogICAgICAgICAgICBrID0gMywgI2RlZmluaWNpw7NuIGRlIGxhIGNhbnRpZGFkIGRlIGNsdXN0ZXIgYSBtYXBlYXIgZW4gZWwgZGVuZG9ncmFtYQogICAgICAgICAgICBib3JkZXIgPSAicmVkIikgI2RlZmluaWNpw7NuIGRlbCBjb2xvciBkZSBsYXMgcmVjdGFzIHF1ZSBzZXBhcmFuIGEgbG9zIGNsdXN0ZXJzCmBgYAoKRXN0YSBncsOhZmljYSBkZW5vbWluYWRhICpkZW5kb2dyYW1hKiBzZSBpbnRlZ3JhIHBvciBkb3MgZGltZW5zaW9uZXMuIFVuYSBob3Jpem9udGFsLCBlbiBsYSBxdWUgc2UgdWJpY2FuIGEgbG9zIGNhc29zLCB5IGVzdMOhbiBvcmRlbmFkb3Mgc2Vnw7puIGxhcyBkaXN0YW5jaWFzIGV4aXN0ZW50ZXMgZW50cmUgY2FkYSB1bm8uIERlIG1hbmVyYSBxdWUgbG9zIGNhc29zIHBvc2ljaW9uYWRvcyB1bm8gYWwgbGFkbyBkZWwgb3RybyBpbmRpY2EgcXVlIHNvbiBjYXNvcyBtdXkgcHLDs3hpbW9zIGVudHJlIHPDrSwgbWllbnRyYXMgcXVlIGRvcyBjYXNvcyBtdXkgc2VwYXJhZG9zLCBvIGluY2x1c2l2ZSBwb3NpY2lvbmFkb3MgZW4gbG9zIGV4dHJlbW9zLCBpbmRpY2EgcXVlIHNvbiBtdXkgZGlzdGFudGVzIGVudHJlIHPDrS5cCgpMYXMgbMOtbmVhcyBkZSBjb25leGnDs24gZXhpc3RlbnRlcyBlbnRyZSBsb3MgY2Fzb3MsIHkgcXVlIHNlIGludGVncmFuIGRlc2RlIGxhIGJhc2UgaGFjaWEgZWwgdG9wZSBzdXBlcmlvciBkZSBsYSBncsOhZmljYSwgc29uIGxhcyBxdWUgZ3LDoWZpY2FtZW50ZSBzZcOxYWxhbiBsb3MgYWdydXBhbWllbnRvcyBvIGNsdXN0ZXJzIHBvc2libGVzIGEgaW50ZWdyYXIsIGFzw60gY29tbyBsb3MgY2Fzb3MgZGVudHJvIGRlIGNhZGEgY2x1c3Rlci5cCgpFbCBlamUgdmVydGljYWwgZGUgbGEgZ3LDoWZpY2EsIG8gbGEgYWx0dXJhIGRlIGxhcyBsw61uZWFzIGRlIGNvbmV4acOzbiByZXByZXNlbnRhIGxhIGRpc3RhbmNpYSBlbnRyZSBjbHVzdGVycy4gRGUgbWFuZXJhIHF1ZSBkaWNoYXMgbMOtbmVhcyBkZSBjb25leGnDs24gdmVydGljYWxlcyByZXByZXNlbnRhbiBjbHVzdGVycyBtaXNtb3MuIFBvciBsbyBxdWUgbMOtbmVhcyBjb24gZGlzdGFuY2lhcyBhbXBsaWFzIGVudHJlIGVsbGFzLCBwb3IgZWplbXBsbywgbGFzIGFsdHVyYXMgbcOhcyBhbXBsaWFzIGVuIHVuIG1pc21vIG5pdmVsIGRlIGNsdXN0ZXJzIG5vcyBwcm9wb3JjaW9uYW4gZWwgbsO6bWVybyBkZSBjbHVzdGVycyBxdWUgbWVqb3IgcmVwcmVzZW50YSBhIG51ZXN0cm9zIGRhdG9zLlwKCkVuIGVsIGVqZW1wbG8gcXVlIGVzdGFtb3MgcmV2aXNhbmRvLCBsYSBtYXlvciBkaXN0YW5jaWEgbyBhbHR1cmEgZW50cmUgbGFzIGzDrW5lYXMgZGUgY29uZXhpw7NuIHZlcnRpY2FsIHNlIGVuY3VlbnRyYSBlbiBlbCBuw7ptZXJvIGRlIGNsdXN0ZXJzIGVudHJlIDIgeSAzLlwKCgojIyAzLjMuIEludGVncmFjacOzbiBkZSBjYXNvcyBhIGNsdXN0ZXJzIGplcsOhcnF1aWNvcwpVbmEgdmV6IHF1ZSBzZSBoYSBpZGVudGlmaWNhZG8gbGEgY2FudGlkYWQgZGUgKmNsdXN0ZXJzKiBwZXJ0aW5lbnRlcyBhIHBhcnRpciBkZWwgKmRlbmRvZ3JhbWEqLCBlcyBpbXBvcnRhbnRlIGF2YW56YXIgZW4gbGEgcmV2aXNpw7NuIHZpc3VhbCBkZSBsYSBpbnRlZ3JhY2nDs24gZGUgZGljaG9zIGFncnVwYW1pZW50b3MuXAoKUGFyYSBlbGxvLCBwcmltZXJvLCBlcyBpbXBvcnRhbnRlIGdlbmVyYXIgdW4gb2JqZXRvIGVuIGVsIHF1ZSBjYWRhIGdydXBvIHNlYSBhc2lnbmFkbyBhIGNhZGEgY2x1c3RlciwgcGFyYSBsbyBxdWUgc2UgdXRpbGl6YSBlbCBjb21hbmRvIGBjdXRyZWUoKWAsIGEgcGFydGlyIGRlbCBvYmpldG8gbGlzdGEgcHJldmlhbWVudGUgZWxhYm9yYWRvIHksIGFkZW3DoXMsIHNlIGRlZmluZSBsYSBjYW50aWRhZCBkZSBjbHVzdGVycyBhIGludGVncmFyIGNvbiBgayA9IGAuIENvbiBlbGxvIHNlIGNyZWEgdW4gb2JqZXRvIHF1ZSwgcG9zdGVyaW9ybWVudGUsIHB1ZWRlIHNlIGludGVncmFkbyBhbCBkYXRhIGZyYW1lIG9yaWdpbmFsLgoKYGBge3J9CmdydXBvc19qIDwtIGN1dHJlZShmaXQyLCAKICAgICAgICAgICAgICAgICBrID0gMykgIyBzZSBkaXN0cmlidXllbiBsb3MgY2Fzb3MgYSBwYXJ0aXIgZGUgbG9zIGNsdXN0ZXJzIGRlZmluaWRvcwpzdWJfaWRoX3ogPC0gZGF0YS5mcmFtZShzdWJfaWRoX3osIAogICAgICAgICAgICAgICAgICAgICAgICBncnVwb3NfaikgIyBzZSBhw7FhZGVuIGxhcyBjbGFzaWZpY2FjaW9uZXMgamVyw6FycXVpY2FzIGFsIGRhdGEgZnJhbWUgb3JpZ2luYWwKdGFibGUoc3ViX2lkaF96ICQgZ3J1cG9zX2opCmBgYAoKQXPDrSBzZSBwdWVkZSBpZGVudGlmaWNhciBxdWUgOTg0IGNhc29zIGZ1ZXJvbiBhZ3JlZ2Fkb3MgZW4gZWwgKmNsdXN0ZXIqIDEsIDgwMSBjYXNvcyBmdWVyb24gYXNpZ25hZG9zIGFsICpjbHVzdGVyKiAyIHkgNjMzIGNhc29zIGZ1ZXJvbiBpbnRlZ3JhZG9zIGFsICpjbHVzdGVyKiAzKi4KCgojIyAzLjQuIERlc2NyaXBjacOzbiBkZSBsYXMgdmFyaWFibGVzIHBvciBjYXRlZ29yw61hcyBqZXLDoXJxdWljYXMKQSBwYXJ0aXIgZGUgbGEgY3JlYWNpw7NuIGRlIGxvcyBjbHVzdGVycyAqamVyw6FycXVpY29zKiwgdGFtYmnDqW4gc2UgcHVlZGVuIGFuYWxpemFyIGxvcyBlc3RhZMOtc3RpY29zIGRlc2NyaXB0aXZvcyBwYXJhIGlkZW50aWZpY2FyIGxhcyBjYXJhY3RlcsOtc3RpY2FzIGludGVybmFzIGRlIGNhZGEgZ3J1cG8uIFBvciBlamVtcGxvOgoKYGBge3IgZGVzY3JpcHRfamVyYXJxfQphZ2dyZWdhdGUoc3ViX2lkaCwgI3NlbGVjY2nDs24gZGUgdmFyaWFibGVzIG9yaWdpbmFsZXMKICAgICAgICAgIGJ5ID0gbGlzdChzdWJfaWRoX3ogJCBncnVwb3NfaiksICNzZWxlY2Npw7NuIGRlIGNvbHVtbmEgY29uIGNsdXN0ZXJzIGplcsOhcnF1aWNvcwogICAgICAgICAgRlVOID0gbWVhbikgIyBjYWxjdWxhciBsYXMgbWVkaWFzIGRlIGxhcyB2YXJpYWJsZXMgYWdyZWdhZGFzIHBvciBjYWRhIGNsdXN0ZXIKYGBgCgoKIyA0LiBNw6l0b2RvIGRlIGFncnVwYW1pZW50byBiYXNhZG8gZW4gbW9kZWxvcyAobW9kZWwgYmFzZWQgY2x1c3RlcnMpLgoKRXN0ZSBtw6l0b2RvIHBhcmEgY2FsY3VsYXIgbGEgY2FudGlkYWQgaWRlYWwgZGUgKmNsdXN0ZXJzKiBhc3VtZSB1bmEgdmFyaWVkYWQgZGUgbW9kZWxvcyBzb2JyZSBsb3MgZGF0b3MsIGFwbGljYW5kbyBlc3RpbWFjaW9uZXMgZGUgbcOheGltYSB2ZXJvc2ltaWxpdHVkLCBhc8OtIGNvbW8gbW9kZWxvcyBiYXNhZG8gZW4gY3JpdGVyaW9zIGRlIEJheWVzIHBhcmEsIGFzw60sIGlkZW50aWZpY2FyIGVsIG1vZGVsbyBpZGVhbCB5IGVsIG7Dum1lcm8gw7NwdGltbyBkZSAqY2x1c3RlcnMqLlwKCkVzdGUgcHJvY2VzYW1pZW50byBkZSBsb3MgZGF0b3Mgc2UgYXBveWEgZW4gZWwgY29tYW5kbyBgTWNsdXN0KClgcXVlIGVzdMOhIGNvbnRlbmlkbyBlbiBsYSBsaWJyZXLDrWEgW2BNY2x1c3QoKWBdKGh0dHBzOi8vbWNsdXN0LW9yZy5naXRodWIuaW8vbWNsdXN0L2FydGljbGVzL21jbHVzdC5odG1sKSwgcXVlIGNhbGN1bGEgZGl2ZXJzb3MgbW9kZWxvcyBkZSBhY3VlcmRvIG1vZGVsb3MgQklDLCBFTSwgYWdydXBhbWllbnRvcyBqZXLDoXJxdWljb3MgcGFyYSBkaXZlcnNvcyBtb2RlbG9zIGdhdXNzaWFub3MsIGVudHJlIG90cm9zIG3DoXMuXAoKVXN1YWxtZW50ZSBzZSBzdWVsZSBzZWxlY2Npb25hciBhbCBtb2RlbG8geSBjYW50aWRhZCBkZSBjbHVzdGVyIHF1ZSByZWZpZXJhbiBhbCB2YWxvciBtw6FzIGVsZXZhZG8gZGUgQklDLiBZIG1lZGlhbnRlIGxhICJheXVkYSIgZGUgUiBTdHVkaW8gc2UgcHVlZGVuIGNvbnN1bHRhciBsb3MgZGV0YWxsZXMgc29icmUgbG9zIGRpdmVyc29zIG1vZGVsb3MgY2FsY3VsYWRvcyBwb3IgbGEgbGlicmVyw61hIChgaGVscChtY2x1c3RNb2RlbE5hbWVzKWApLgoKCiMjIDQuMS4gQ29uc3RydWNjacOzbiBkZSBsb3MgbW9kZWxvcyAKTGEgbWFuZXJhIGRlIGluaWNpYXIgZWwgYW7DoWxpc2lzIGNvbnNpc3RlIGVuIHV0aWxpemFyIGVsIGNvbWFuZG8gYG1jbHVzdEJJQygpYCBzb2JyZSBlbCBkYXRhIGZyYW1lIHF1ZSBjb250aWVuZSBzb2xvIGEgbGFzIHZhcmlhYmxlcyBkZSBpbnRlcsOpcywgcG9yIGVsbG8gc2UgcmV0b21hIGVsIG9iamV0byBlc3RhbmRhcml6YWRvIGBzdWJfaWRoX3pgLiBBIHBhcnRpciBkZSBlc3RvIHNlIGVsYWJvcmFuIGxvcyBkaXZlcnNvcyBtb2RlbG9zIHF1ZSBzZSBjb21wYXJhcsOhbiBlbiBsYSBjb25zdHJ1Y2Npw7NuIGRlIGxvcyBjbHVzdGVycy5cCgpQb3N0ZXJpb3JtZW50ZSBzZSBzb2xpY2l0YSBxdWUgc2UgZ3JhZmlxdWVuIGxvcyByZXN1bHRhZG9zIGNvbiBgcGxvdCgpYC4KCmBgYHtyfQpsaWJyYXJ5KG1jbHVzdCkKQklDIDwtIG1jbHVzdEJJQyhzdWJfaWRoX3pbLCAxOjRdKSAjIHNlbGVjY2nDs24gZGUgbGFzIHZhcmlhYmxlcyBlc3RhbmRhcml6YWRhcwpwbG90KEJJQykKYGBgCgpFc3RlIHRpcG8gZGUgYW7DoWxpc2lzIHNlIGFwb3lhLCBhc8OtIGNvbW8gbG9zIGFudGVyaW9yZXMsIGVuIGxhIHJldmlzacOzbiBkZSByZXN1bHRhZG9zIGdyw6FmaWNvcy4gRWwgZ3LDoWZpY28gZ2VuZXJhZG8gbXVlc3RyYSBsb3MgZGl2ZXJzb3MgbW9kZWxvcyBkZSBhanVzdGUgZGUgbG9zIGRhdG9zIHF1ZSBzZSBjb3JyacOzIGNvbiBlbCBjb21hbmRvIGBtY2x1c3RCSUMoKWAuXCAKCkVuIGVsIHBsYW5vIGhvcml6b250YWwgZGUgbGEgZ3LDoWZpY2EgQklDIHNlIG9ic2VydmEgZWwgbsO6bWVybyBkZSBjb21wb25lbnRlcyBvICpjbHVzdGVycyogZXZhbHVhZG9zLCBtaWVudHJhcyBxdWUgZW4gZWwgZWplIHZlcnRpY2FsIHNlIG11ZXN0cmEgZWwgcHVudGFqZSBCSUMuIERlbnRybyBkZSBsYSBncsOhZmljYSBzZSBtdWVzdHJhbiBsb3MgcHVudGFqZXMgQklDIGRlIGxvcyBkaXN0aW50b3MgbW9kZWxvcyBldmFsdWFkb3MgKHN1cyBzaWdsYXMgc2UgbXVlc3RyYW4gZW4gZWwgcmVjdWFkcm8gZGUgdGV4dG8gZGVudHJvIGRlIGxhIGdyw6FmaWNhKS5cIAoKWSBlc3RvIHBlcm1pdGUgaWRlbnRpZmljYXIgdmlzdWFsbWVudGUgZWwgbW9kZWxvIGFzw60gY29tbyBsYSBjYW50aWRhZCDDs3B0aW1hIGRlIGNsdXN0ZXJzIGEgaW50ZWdyYXIuIEVzdG8gc2UgZGVmaW5lIG1lZGlhbnRlIGxhIGlkZW50aWZpY2FjacOzbiBkZWwgbW9kZWxvIHF1ZSByZWdpc3RyYSBlbCBuaXZlbCBtw6FzIGFsdG8gZW4gcHVudGFqZSBCSUMsIGFzw60gY29tbyBzdSBwb3NpY2nDs24gc29icmUgZWwgZWplIGhvcml6b250YWwsIGVuIGxhIHF1ZSBzZSBkZWZpbmUgbGEgY2FudGlkYWQgZGUgY2x1c3RlcnMuXAoKCiMjIDQuMi4gRXZhbHVhY2nDs24gZGUgbG9zIGRlc2NyaXB0aXZvcyBkZSBsb3MgbW9kZWxvcwoKTGEgbWFuZXJhIHBhcmEgY29uZmlybWFyIGVzdGEgZGVjaXNpw7NuIHZpc3VhbCBjb25zaXN0ZSBlbiBzb2xpY2l0YXIgdW4gYHN1bW1hcnkoKWAsIGEgcGFydGlyIGRlIGNhbGN1bGFyIGxvcyBlc3RhZMOtc3RpY29zIGRlIGNhZGEgdW5vIGRlIGxvcyBtb2RlbG9zIGV2YWx1YWRvcy4gUGFyYSBlbGxvIHByaW1lcm8gc2UgZGViZSBhcGxpY2FyIGVsIGNvbWFuZG8gYE1jbHVzdCguLi4sIHggPSBCSUMpYCwgcXVlIHBlcm1pdGUgZ2VuZXJhciBsb3MgZXN0YWTDrXN0aWNvcyBkZSBsb3MgbW9kZWxvcywgYXPDrSBjb21vIGxhIGNhbnRpZGFkIMOzcHRpbWEgZGUgKmNsdXN0ZXJzKi4gRXN0b3MgcmVzdWx0YWRvcyBzZSBndWFyZGFuIGNvbW8gb2JqZXRvIHBhcmEsIHBvc3Rlcmlvcm1lbnRlLCBzb2xpY2l0YXIgbGEgcHJlc2VudGFjacOzbiBkZSBsb3MgcmVzdWx0YWRvcy4KCmBgYHtyfQptb2QxIDwtIE1jbHVzdChzdWJfaWRoLCB4ID0gQklDKQpzdW1tYXJ5KG1vZDEsIHBhcmFtZXRlcnMgPSBUUlVFKQpgYGAKCkEgcGFydGlyIGRlIGxvcyByZXN1bHRhZG9zIHByZXNlbnRhZG9zIGVzIGltcG9ydGFudGUgb2JzZXJ2YXIgbGEgKnRhYmxhIGRlIGFncnVwYW1pZW50byogKG8gKmNsdXN0ZXJpbmcgdGFibGUqKSwgZW4gZG9uZGUgc2UgZGVmaW5lIGxhIGNhbnRpZGFkIMOzcHRpbWEgZGUgKmNsdXN0ZXJzKi4gRW4gZWwgZWplcmNpY2lvIHJlYWxpemFkbyBjb24gbG9zIGRhdG9zIGRlbCBJREggc2UgaWRlbnRpZmljYW4gNiAqY2x1c3RlcnMqIGNvbW8gw7NwdGltb3MuCgpGaW5hbG1lbnRlLCBlc3RlIGFncnVwYW1pZW50byBzZSBwdWVkZSByZXZpc2FyIHZpc3VhbG1lbnRlIGVuIHVuYSBncsOhZmljYSBkZSBkaXNwZXJzacOzbiBlbiBsYSBxdWUsIGEgc3UgdmV6LCBzZSBzb2xpY2l0ZSBxdWUgbG9zIGNhc29zIHNlYW4gcmVwcmVzZW50YWRvcyBkZW50cm8gZGUgY2FkYSAqY2x1c3RlciogbWVkaWFudGUgZWwgY29tYW5kbyBgcGxvdCguLi4sIHdoYXQgPSAiY2xhc3NpZmljYXRpb24iKWAuCgpgYGB7cn0KcGxvdChtb2QxLCB3aGF0ID0gImNsYXNzaWZpY2F0aW9uIikKYGBgCgpEZSBlc3RhIG1hbmVyYSBzZSBoYW4gaWRlbnRpZmljYWRvIHVuYSBtYXlvciBjYW50aWRhZCBkZSAqY2x1c3RlcnMqLCB5IHF1ZSBwZXJtaXRlIHVuYSBjbGFzaWZpY2FjacOzbiBtw6FzICJmaW5hIiBkZWwgY29tcG9ydGFtaWVudG8gZGUgbG9zIGRhdG9zIGVuIGNvbXBhcmFjacOzbiBkZWwgdHJhYmFqbyBtaXNtbyBxdWUgZWxhYm9yw7MgZWwgUE5VRCBwYXJhIGVsIGNhc28gbWV4aWNhbm8sIGVuIGRvbmRlIGFncnVww7MgYSBsb3MgY2Fzb3MgZW4gMyBjYXRlZ29yw61hcyAoSURIIGFsdG8sIG1lZGlvIHkgYmFqbykuCg==