Integrantes
- Victor Diaz Olivo
- Miller Sarsoza Albino
- David Huamán Pasapera
- Carlos Eduardo Lazo Herrera
Resumen
El presente proyecto evalúa variables relacionadas a niñas y niños
menores de 1 a 3 años de la Región de Ayacucho, que fueron atendidos en
los establecimientos de salud de Dirección de Regional de Salud (Diresa)
respectiva, en el año 2021, con el objeto de obtener perfiles
nutricionales que permitan focalizar el servicio de los centros de salud
respectivos. Se aplicaron tres (3) metodologías de agrupamiento: Cluster
Jerarquico Agnes, cluster basado en particiones K-means y algoritmo
BIRCH, obteniendo que la mejor agrupación está dada por el segundo
algoritmo.
Introducción
La desnutrición crónica en la infancia es uno de los principales
problemas de salud pública y un indicador de desarrollo social del país;
tiene efectos irreversibles en el desarrollo de habilidades y
capacidades en la niña y el niño.
El INEI en el informe “Perú: Indicadores de resultados de los
programas presupuestales, 2015-2020” señala que el 12.1% de la población
menor de cinco años de edad del país sufrió desnutrición crónica el año
2020, siendo 0.1% menor que el 2019 y -2.3% respecto al 2015,
observándose que no hubo avances significativos. A su vez, el mismo
informe señala que el 40% de niños y niñas entre 6 a 35 meses del mismo
año presentaron prevalencia de anemia[1].
El diseño de políticas públicas, y en específico, el desarrollo de
programas y proyectos sociales, para mejorar el estado nutricional de
los menores de cinco años es una prioridad para el desarrollo nacional;
siendo importante la generación de investigaciones que permitan conocer
sus perfiles nutricionales. En este marco, el análisis estádistico de
datos tiene mucho por contribuir.
En ese sentido el presente trabajo tiene como objetivo la obtención
de clústeres para conocer los perfiles nutricionales de niños y niñas
menores de 1 a 3 años que fueron atendidos en establecimientos de salud
de primer nivel de la Diresa Ayacucho el año 2021, con fines de
identificar patrones que permitan la planificación de intervenciones
focalizadas que contribuyan a mejorar su calidad de vida.
Descripción de los datos
Los datos corresponden a información de historias clínicas de los
establecimientos de salud del Ministerio de Salud del Perú (Minsa), y
que es registrada en el Sistema de Información del Estado Nutricional
(SIEN), administrado por el Centro Nacional de Alimentación y Nutrición
(CENAN), órgano de línea técnico normativo del Instituto Nacional de
Salud (INS). La base de datos que usamos cuenta con un total de
““7,599”” registros de menores de 1 a 3 años y fue tomada de la
Plataforma Nacional de Datos Abiertos[2].
Fuente
Unidad de análisis
Niñas y niños menores de 1 a 3 años que fueron atendidos en los
establecimientos de salud del primer nivel de atención de la Diresa
Ayacucho el año 2021.
Variables
Las variables utilizadas en el análisis son las siguientes:
Variables predictoras: Edad/meses: Edad en meses del
menor atendido (de 12 a 36 meses). Peso (kg). Talla (cm).
PTZ: Índice que compara el peso del menor con el peso esperado para
su talla y permite establecer si ha ocurrido una pérdida/ganancia de
peso corporal. ZTE: Índice que compara la talla del menor con la
talla esperada para su edad y permite establecer si está ocurriendo un
retraso en el crecimiento. ZPE: Índice que compara el peso del
menor con el peso esperado para su edad y permite establecer si está
ocurriendo desnutrición. IMC: Índice de masa corporal.
Hemoglobina (g/dL) Hemoglobina ajustada: Hemoglobina ajustada
según la altura de la localidad de residencia del menor. AlturaREN:
Metros sobre el nivel del mar de la localidad de residencia del
menor.
Adicionalmente se utilizó la variable Sexo del menor y Anemia para el
análisis del perfilado obtenido.
Debemos señalar que la variable anemia se calculó a partir de la
variable hemoglobina ajustada, que a su vez fue calculada de la variable
hemoglobina siguiendo las recomendaciones del Ministerio de Salud - Perú
para concentraciones de hemoglobina en función de la altitud sobre el
nivel del mar, incorporando un factor de ajuste por altitud[3]. Lugo se
realizó el cálculo de la variable Anemia en base a la variable
Hemoglobina ajustada para analizar los resultados del algoritmo K-means
por clusters, usando valores de corte con base en las concentraciones de
hemoglobina para diagnosticar anemia al nivel del mar (g/l)± que
recomienda la OMS[4].
Metodología
El nombre BIRCH viene de Balanced Iterative Reducing and Clustering
Using Hierarchies, es un método de agrupamiento jerárquico que trabaja
muy bien con sets de datos masivos dado que agrupa de forma incremental
y dinámica los datos entrantes para dar el mejor agrupamiento con los
recursos disponibles, del mismo modo una característica principal del
algoritmo es que escanea los datos una sola vez[5].
Los conceptos más relevantes con respecto al algoritmo son los
siguientes:
- Clustering Feature: Si se tiene un set de N datos, el clustering
feature se define como:
\(CF=(N,\bar{LS},SS)\)
Donde:
\(\bar{LS}=\sum_{i=0}^n\bar{X_i}\)
\(SS=\sum_{i=0}^n\bar{X_i}^2\)
Si se tienen los datos \((x_1);(x_2);(x_3);(x_4)\)
El valor N será igual a 4
El valor de \(\bar{LS}\) es
igual a \((x_1+x_2+x_3+x_4)\)
El valor de \(SS\) será igual a
\((x_1^2+x_2^2+x_3^2+x_4^2)\)
De esta forma al final los puntos de muestra están representados
como \(CF=(N,\bar{LS},SS)\)
- Clustering Feature Tree (CFT): Con los datos generados por el
Clustering Feature de construye un árbol de clústers en orden jerárquico
de tal modo que las hojas previas representan clústers de mayor tamaño,
y que la división de estos produzcan a su vez nuevos nodos (clústers) de
menor tamaño.
El procedimiento culmina una vez que los clústers alcancen un
tamaño inferior a un parámetro dado o se logre el número de clústers
deseado[6].
El algoritmo Birch cuenta con dos principales parámetros
- Branching factor (B): Cantidad máxima de subclústers en cada nodo,
en caso de superar este factor el nodo se divide en dos nodos con los
subclústers redistribuidos.
- Threshold (T): Es el umbral establecido para la creación de un nuevo
subclúster. Establecer este valor bajo genera la creación de más
clústers.
Ejemplo de aplicación del Algoritmo
Considerando los siguientes valores:
Paso 1: Se elige el Treshold (5).
Paso 2: Se elige el Branching factor (3).
Paso 3: Se toma el primer valor y se eleva al
cuadrado.
Paso 4: Se eleva al cuadrado ambos valores y se
suma.
Paso 5: El centroide es igual a la suma de los
valores sobre N, para el primer valor el centroide sería:
\(Centroide_0 = 22/1 = 22\)
Paso 6: Se aplica la fórmula
\(D=\sqrt{\frac{\sum_{i=1}^n\sum_{j=1}^n(x_i-x_j)^2}{n(n-1)}}\)
\(D=\sqrt{\frac{2n(SS)-2(LS)^2}{n(n-1)}}\)
\(D=\sqrt{\frac{2*2(565)-2(31)^2}{2(2-1)}}\)
Entonces \(D=13\), que es superior
al treshold, por lo tanto, se crea un nuevo clúster.
Paso 7: Trabajando el siguiente valor: 12
\(Centroide_0 = 22/1 = 22\)
\(Centroide_1 = 9/1 = 9\)
El punto 12 esta más cerca a 9.
\(D=\sqrt{\frac{2*2(225)-2(21)^2}{2(2-1)}}\)
\(D=3\)
Como \(D=3<5\), se juntan los
puntos 9 y 12.
Paso 8: Trabajando el siguiente valor: 15
\(Centroide_2 = (9+12)/2 =
10.5\)
\(Centroide_0 = (22)/1 = 22\)
El punto 12 esta más cerca al \(Centroide_2=10.5\).
9, 12 |
2 |
21 |
225 |
9, 12, 15 |
3 |
36 |
450 |
\(D=\sqrt{\frac{2*3(450)-2(36)^2}{2(2-1)}}\)
\(D=4.24\)
Como \(D=4.24<5\), se juntan los
puntos 9, 12 y 15.
\(Centroide_3 = (9+12+15)/3 =
12\)
\(Centroide_0 = (22)/1 = 22\)
Ejemplo en Python
#Configuración del entorno
rm(list = ls())
setwd(dirname(rstudioapi::getActiveDocumentContext()$path))
graphics.off()
options(scipen = 999)
options(digits = 3)
path_python="C:/Users/ASUS/anaconda3"
#Cargando paquetes necesarios
library(pacman)
p_load(reticulate, PerformanceAnalytics, purrr, skimr, corrplot, cluster, psych, ggplot2,
stream, ellipse, tictoc,factoextra, NbClust, BiocManager, naniar, DataExplorer,
tidyverse, purrr, dplyr, readxl, readr,stats, DescTools, class,devtools,imager,knitr,kableExtra)#source,compareGroups
#Cargando funciones de usuario
source("funciones.R")
library(reticulate)
use_python(path_python)
import pandas as pd
from sklearn.cluster import Birch
df_ejemplo = pd.DataFrame([22,9,12,15],columns=['Puntos'])
brc0 = Birch(threshold=5,
branching_factor=3
)
brc0.fit(df_ejemplo)
Birch(branching_factor=3, threshold=5)
C:\Users\ASUS\ANACON~1\lib\site-packages\sklearn\cluster\_birch.py:717: ConvergenceWarning: Number of subclusters found (2) by BIRCH is less than (3). Decrease the threshold.
warnings.warn(
pd.concat([df_ejemplo,pd.DataFrame(brc0.predict(df_ejemplo),columns=['Cluster'])],axis=1)
Puntos Cluster
0 22 0
1 9 1
2 12 1
3 15 1
Lectura de datos
# Cargamos la data
ayacucho <- read.csv("Data.csv")
head_5=head(ayacucho)
kable(head_5,caption = "Ayacucho") %>% kable_styling("striped") %>% scroll_box(width = "100%")
Ayacucho
Diresa
|
Microred
|
EESS
|
Dpto_EESS
|
Prov_EESS
|
Dist_EESS
|
Renipress
|
FechaAtencion
|
Sexo
|
FechaNacimiento
|
Juntos
|
SIS
|
Qaliwarma
|
EdadMeses
|
Peso
|
Talla
|
IMC
|
PTZ
|
ZTE
|
ZPE
|
Hemoglobina
|
AlturaREN
|
Hemoglobinaajustada
|
Cred
|
Suplementacion
|
Consejeria
|
Sesion
|
AYACUCHO
|
SANTA ROSA
|
I-1 - 00003765 - PUESTO DE SALUD COMUNPIARI
|
AYACUCHO
|
LA MAR
|
SANTA ROSA
|
3765
|
12/31/2021
|
F
|
12/31/2019
|
NA
|
NA
|
NA
|
24
|
12.0
|
76.0
|
15.8
|
2.53
|
-3.02
|
0.35
|
10.7
|
330
|
10.7
|
1
|
0
|
0
|
0
|
AYACUCHO
|
SAN MARTIN
|
I-3 - 00003762 - CENTRO DE SALUD SAN MARTIN
|
AYACUCHO
|
LA MAR
|
ANCO
|
3762
|
10/06/2021
|
F
|
3/24/2020
|
NA
|
NA
|
NA
|
19
|
11.8
|
79.7
|
14.9
|
1.80
|
-0.49
|
1.09
|
11.5
|
3215
|
9.4
|
1
|
0
|
0
|
0
|
AYACUCHO
|
PAMPA CANGALLO
|
I-4 - 00003507 - CENTRO DE SALUD PAMPA CANGALLO
|
AYACUCHO
|
CANGALLO
|
LOS MOROCHUCOS
|
3507
|
6/14/2021
|
M
|
05/05/2020
|
NA
|
NA
|
NA
|
13
|
8.9
|
73.3
|
12.1
|
-0.34
|
-1.63
|
-1.01
|
14.4
|
3330
|
12.1
|
1
|
1
|
0
|
0
|
AYACUCHO
|
NO PERTENECE A NINGUNA MICRORED
|
II-E - 00003575 - HOSPITAL JESUS NAZARENO
|
AYACUCHO
|
HUAMANGA
|
JESUS NAZARENO
|
3575
|
8/27/2021
|
M
|
5/27/2020
|
NA
|
NA
|
NA
|
15
|
8.4
|
74.7
|
11.2
|
-1.44
|
-1.76
|
-1.86
|
12.4
|
2780
|
10.8
|
0
|
1
|
0
|
0
|
AYACUCHO
|
SAN MARTIN
|
I-3 - 00003762 - CENTRO DE SALUD SAN MARTIN
|
AYACUCHO
|
LA MAR
|
ANCO
|
3762
|
10/30/2021
|
M
|
7/25/2020
|
NA
|
NA
|
NA
|
15
|
10.5
|
76.4
|
13.7
|
0.82
|
-1.16
|
0.11
|
11.7
|
3215
|
9.6
|
0
|
1
|
0
|
0
|
AYACUCHO
|
SANTA ROSA
|
I-4 - 00003764 - SANTA ROSA
|
AYACUCHO
|
LA MAR
|
SANTA ROSA
|
3764
|
9/20/2021
|
F
|
1/20/2019
|
NA
|
NA
|
NA
|
32
|
13.5
|
88.8
|
15.2
|
0.99
|
-0.94
|
0.24
|
11.6
|
330
|
11.6
|
1
|
1
|
0
|
0
|
str(ayacucho)
'data.frame': 7599 obs. of 27 variables:
$ Diresa : chr "AYACUCHO" "AYACUCHO" "AYACUCHO" "AYACUCHO" ...
$ Microred : chr "SANTA ROSA" "SAN MARTIN" "PAMPA CANGALLO" "NO PERTENECE A NINGUNA MICRORED" ...
$ EESS : chr "I-1 - 00003765 - PUESTO DE SALUD COMUNPIARI" "I-3 - 00003762 - CENTRO DE SALUD SAN MARTIN" "I-4 - 00003507 - CENTRO DE SALUD PAMPA CANGALLO" "II-E - 00003575 - HOSPITAL JESUS NAZARENO" ...
$ Dpto_EESS : chr "AYACUCHO" "AYACUCHO" "AYACUCHO" "AYACUCHO" ...
$ Prov_EESS : chr "LA MAR" "LA MAR" "CANGALLO" "HUAMANGA" ...
$ Dist_EESS : chr "SANTA ROSA" "ANCO" "LOS MOROCHUCOS" "JESUS NAZARENO" ...
$ Renipress : int 3765 3762 3507 3575 3762 3764 3600 3782 3783 3603 ...
$ FechaAtencion : chr "12/31/2021" "10/06/2021" "6/14/2021" "8/27/2021" ...
$ Sexo : chr "F" "F" "M" "M" ...
$ FechaNacimiento : chr "12/31/2019" "3/24/2020" "05/05/2020" "5/27/2020" ...
$ Juntos : int NA NA NA NA NA NA NA NA NA NA ...
$ SIS : int NA NA NA NA NA NA NA NA NA NA ...
$ Qaliwarma : int NA NA NA NA NA NA NA NA NA NA ...
$ EdadMeses : int 24 19 13 15 15 32 19 27 19 30 ...
$ Peso : num 12 11.8 8.9 8.4 10.5 ...
$ Talla : num 76 79.7 73.3 74.7 76.4 88.8 77 80 77 90.5 ...
$ IMC : num 15.8 14.9 12.1 11.2 13.7 ...
$ PTZ : num 2.53 1.8 -0.34 -1.44 0.82 0.99 -0.5 0.07 -0.63 0.82 ...
$ ZTE : num -3.02 -0.49 -1.63 -1.76 -1.16 -0.94 -2.12 -2.45 -1.54 -0.31 ...
$ ZPE : num 0.35 1.09 -1.01 -1.86 0.11 0.24 -1.35 -1.33 -1.2 0.44 ...
$ Hemoglobina : num 10.7 11.5 14.4 12.4 11.7 ...
$ AlturaREN : int 330 3215 3330 2780 3215 330 2800 3499 3499 2734 ...
$ Hemoglobinaajustada: num 10.7 9.4 12.1 10.8 9.6 ...
$ Cred : int 1 1 1 0 0 1 1 1 0 1 ...
$ Suplementacion : int 0 0 1 1 1 1 0 0 0 1 ...
$ Consejeria : int 0 0 0 0 0 0 0 0 0 1 ...
$ Sesion : int 0 0 0 0 0 0 0 0 0 0 ...
#skim(ayacucho)
#describe(ayacucho)
Preprocesamiento de datos
# Factorización de variables no númericas
variables_fac <- c("Diresa", "Microred", "EESS", "Dpto_EESS", "Prov_EESS", "Dist_EESS", "Renipress", "Sexo", "Juntos", "SIS", "Qaliwarma", "Cred", "Suplementacion", "Consejeria", "Sesion")
ayacucho[,variables_fac] <- lapply(ayacucho[,variables_fac], factor)
#str(ayacucho) cambio
Datos Perdidos
aya <-ayacucho[,14:23]
plot_missing(aya)

El análisis muestra que las variables seleccionadas no presentan
valores perdidos o faltantes, por lo tanto, no se requiere realizar un
procedimiento de imputación.
Analisis de Outliers
gg_box_density(aya)


A partir de los gráficos anteriores se observa que todas las
variables a excepción de EdadMeses poseen valores outliers y extremos en
algunos casos, como las variables hemoglobina y AlturaRen y
Hemoglobinaajustada.
Tratamiento de datos Outliers
cols=c('Peso','Talla','IMC','PTZ','ZTE','ZPE','Hemoglobina','AlturaREN','Hemoglobinaajustada')
aya=fn_outliers(aya,cols,2,1.5)
gg_box_density_2(aya)


El procedimiento para tratar los valores outliers y extremos
consistió en acotar los valores superiores y inferiores de las variables
mediante el principio de 1.5 veces el rango intercuartílico, sumando
este valor al percentil 75 para definir el valor superior de la variable
y restando el valor indicado al percentil 25 para definir el valor
inferior máximo aceptado.
Los valores que fueron superiores o inferiores a los valores en
el rango definidos fueron reemplazados por el máximo y mínimo calculado
de dicho rango.
Correlación
#cor <- ayacucho[,14:23]%>% chart.Correlation(histogram=TRUE, pch=15)
correlacion<-round(cor(ayacucho[,14:23]), 2)
corrplot(correlacion, method="number", type="upper",number.cex = 0.72,tl.cex = 0.8,addCoef.col = 0.5)

De acuerdo con el análisis de correlación de Pearson se
interpreta que no existen relación lineal negativa entre las
variables.
Existe una correlación lineal positiva fuerte entre las
variables:
EdadMeses y Talla: 0.86 Peso y Talla: 0.84 Peso
e IMC: 0.92 IMC y PTZ: 0.82 IMC y ZPE: 0.78
PTZ y ZPE: 0.84 Hemoglobina y Hemoglobinaajustada:
0.84
Existe una correlación lineal positiva moderada entre las
variables: EdadMeses y Peso: 0.69 EdadMeses y IMC:
0.44 Peso y PTZ: 0.55 Peso y ZTE: 0.42 Peso y
ZPE: 0.61 Talla y ZPE: 0.42 Hemoglobina y AlturaRen:
0.44
Estandarización de los datos
aya <- as.data.frame(scale(aya))
Determinando factibilidad del cluster
Matriz de distancia euclidiana
dis.Data <- dist(aya, metric = c("euclidean")) # Matriz de distancia #cambio
#dis.Data <- daisy(aya, metric= "euclidean",stand = TRUE) # Matriz de distancia
Visualizando la matriz de distancia con fviz_dist()
library(imager)
if (file.exists("graf_max.dist.png")) { #Cambio
img <- load.image('graf_max.dist.png')
plot(img)
}

#else{
# fviz_dist(dis.Data)
# }
Se realizó el cálculo y visualización la matriz de distancia
euclidiana utilizando las funciones fviz_dist () en el paquete
factoextra r. Se observan áreas sombreadas de color rojo que
indican la cercanía de las observaciones en base al cálculo de distancia
indicado. Esto permite inferir que el set de datos es
agrupable.
Estadístico Hopkins
re_process=F
if (file.exists("res.Hopkins.RData") & !re_process) { #Cambio
load('res.Hopkins.RData')
}else{
res.Hopkins <- get_clust_tendency(aya,
n = nrow(aya) - 1,
graph = FALSE, seed = 2022)
save(res.Hopkins,file='res.Hopkins.RData')
}
res.Hopkins$hopkins_stat
[1] 0.853
Se realizó la prueba estadística de Hopkins, utilizando 0,5 como
el umbral para determinar que es poco probable que el set de datos tenga
conglomerados estadísticamente significativos. Observamos que el valor
resultante de 0.853 se acerca a 1, entonces podemos concluir que el
conjunto de datos es significativamente agrupable.
Análisis cluster jerarquico (hclust {stats})
Analizando el metodo de enlace optimo (coeficiente de aglomeración)
metodos= c("single", "complete", "average", "ward.D", "ward.D2")
COEF.AGL(dis.Data,metodos)

single complete average ward.D ward.D2
0.788 0.954 0.888 1.000 0.997
Hacemos uso de la comparación de los coeficientes de aglomeración
para determinar el número de conglomerados y proponer el mejor esquema
de agrupación a partir de los diferentes resultados obtenidos al variar
todas las combinaciones de métodos de enlace: “single”, “complete”,
“average”, “ward.D”, “ward.D2”. Con base en el resultado en la gráfica
se puede indicar que el enlace optimo es el de: Ward.D
Analizando el metodo de enlace optimo (matriz cofenetica)
#Debido a que graficamente no estan muy alejados con coeficientes de aglomeracion, probaremos el metodo de
#correlacion cofenetica para definir el mejor metodo de enlace
re_process=FALSE
if(file.exists("df_cof.RData") & !re_process) { #Cambio
load('df_cof.RData')
}else{
metodos=c("ward.D", "ward.D2","complete", "average", "single")
df_cof=fn_confenetico(dis.Data,metodos)
save(df_cof,file='df_cof.RData')
}
kable(df_cof,caption = "Correlacion_cofenetica")%>% kable_styling("striped")
Correlacion_cofenetica
Metodo
|
Correl
|
average
|
0.551
|
single
|
0.464
|
ward.D2
|
0.393
|
ward.D
|
0.360
|
complete
|
0.350
|
Realizamos el análisis de la matriz cofenetica para determinar la
similaridad entre la matriz de distancia original (euclidiana) y la
matriz de las uniones de las observaciones en los cluster jerárquicos
efectuados con los métodos de enlaces iniciales. Como resultado se
observa que no hay una correlación fuerte pero el método average
presenta la mayor correlación. Se decide evaluar el análisis de cluster
jerárquico con ambos métodos: Ward.D y average.
Obteniendo el cluster con el metodo: Ward.D
Clus_AG_W <- hclust(dis.Data,method="ward.D")
Determinando de manera grafica el numero de clusters
library(gghighlight)
longitud=length(Clus_AG_W$height)
alturas <- data.frame(etapa = 1:longitud, distancia = Clus_AG_W$height)
ggplot(alturas) + aes(x = etapa, y = distancia) +
geom_point() + geom_line() +
scale_x_continuous(breaks = seq(1, longitud, 1000)) +
geom_vline(xintercept = 7597, col = "red", lty = 2) +
geom_text(aes(label = round(distancia,1)),
size = 3, hjust= +1, vjust= -1) +
theme_classic() #+ gghighlight(distancia > 11)
El método grafico nos sugiere la existencia de 2 a 5
agrupaciones.
Determinado el número de clusters optimo
re_process=F
if (file.exists("res.nbclustW.RData")& !re_process) { #Cambio
load('res.nbclustW.RData')
}else{
seed = 2022
res.nbclustW <- NbClust(aya, distance ="euclidean",
min.nc = 2, max.nc = 8, method = "ward.D", index ="all") #Cambio
save(res.nbclustW,file='res.nbclustW.RData')
}
par(mfrow=c(1,1))
fviz_nbclust_x(res.nbclustW)
Among all indices:
===================
* 2 proposed 0 as the best number of clusters
* 1 proposed 1 as the best number of clusters
* 6 proposed 2 as the best number of clusters
* 8 proposed 3 as the best number of clusters
* 1 proposed 4 as the best number of clusters
* 1 proposed 5 as the best number of clusters
* 3 proposed 6 as the best number of clusters
* 2 proposed 8 as the best number of clusters
* 2 proposed NA's as the best number of clusters
Conclusion
=========================
* According to the majority rule, the best number of clusters is 3 .

Mediante el uso de paquete NbClust podemos determinar el mejor
número de agrupaciones tomando en cuenta los diferentes resultados
obtenidos al variar todas las combinaciones la cantidad de clúster
deseados para el método de enlace elegido: Ward.D.
Realizando clustering con K=3
# Cortando en 2 cluester
grp_WR=cutree(Clus_AG_W, k = 3)
# Number de casos en cada cluster
table(grp_WR)
grp_WR
1 2 3
3361 2919 1319
# Descripción de cada cluster
med<-aggregate(aya_bkp, by=list(cluster=grp_WR), mean) #medias
kable(med) %>% kable_styling("striped") %>% scroll_box(width = "100%")
cluster
|
EdadMeses
|
Peso
|
Talla
|
IMC
|
PTZ
|
ZTE
|
ZPE
|
Hemoglobina
|
AlturaREN
|
Hemoglobinaajustada
|
1
|
26.5
|
12.66
|
86.2
|
14.7
|
0.785
|
-0.673
|
0.240
|
13.3
|
2797
|
11.8
|
2
|
19.9
|
9.99
|
78.7
|
12.7
|
-0.149
|
-1.399
|
-0.796
|
12.5
|
2827
|
11.0
|
3
|
30.9
|
11.32
|
85.6
|
13.2
|
-0.400
|
-1.803
|
-1.273
|
13.7
|
2986
|
11.9
|
#knitr::kable(med, format = "markdown")
Se procedió a cortar la agrupación general (dendograma) en 03
clústers tomando como base el resultado óptimo de Nbclust y obtenemos la
cantidad de observaciones por cada uno de ellos.
Caracterízando los cluster
Graficando los clusters.
#fviz_dend(Clus_AG, k = 2, cex = 0.7, horiz = FALSE, k_colors = "jco",
#rect = TRUE, rect_border = "jco", rect_fill = TRUE)
Diagrama de caracterización - lineas
data_plot=scale(aya_bkp) #Scale
#data_plot=apply(aya_bkp, 2, normalize) #Max-Min
M<-as.data.frame(t(rbind(aggregate(data_plot, by=list(cluster=grp_WR), mean)[,-1])))
a=as.vector(colMeans(data_plot))
fin=data.frame(M,a,names(aya_bkp));names(fin)<-c("Clus1","clus2","clus3","Media","var")
#fin=data.frame(M,a,names(aya_bkp));names(fin)<-c("Clus1","clus2","Media","var")
ali=melt(fin,id.vars = "var")
agnes3 <- ggplot(ali, aes(x=var,y=round(value,1),group=variable,colour=variable)) +
geom_point()+ geom_line(aes(lty=variable))+ expand_limits(y = c(-1.9, 1.9))+
theme(axis.text.x = element_text(angle = 60, vjust = 0.5, hjust=1)) +
labs(title="Diagrama de lineas de cluster AGNES por variable cuantitativa con K=3",
x="Variable", y = "valor")
agnes3

Se calcula los promedios de las observaciones con la base de
datos original agrupado por cada clúster. Se observa que el
clúster 01 tiene mayor cantidad de individuos y estos se caracterizan
por tener mayor peso, talla, el mínimo retraso en su crecimiento, un
índice nutricional positivo, mayor índice de masa corporal y viven a una
altura menor. Por otro lado, se puede observar que el clúster
03 posee los individuos de mayor edad, pero, el peso, el índice de masa
corporal, talla es menor al clúster 01 y poseen mayor
desnutrición.
Diagrama de caracterización - boxplot
dd <- cbind(aya_bkp, cluster =grp_WR )
dd$cluster<-as.factor(dd$cluster)
df.m <- melt(dd, id.var = "cluster")
p <- ggplot(data = df.m, aes(x=variable, y=value)) +
geom_boxplot(aes(fill=cluster))+ facet_wrap( ~ variable, scales="free")
p

Las gráficas permiten analizar los clústeres de manera individual
a través de sus variables. Por ejemplo, el clúster con mayor altura de
residencia es el número 03. El clúster con menor talla 03, el clúster
con mayor índice de masa corporal es el clúster 01.
Obteniendo el cluster con el metodo: average
Clus_AG_AV <- hclust(dis.Data,method="average")
Determinando de manera grafica el numero de clusters
library(gghighlight)
longitud=length(Clus_AG_AV$height)
alturas <- data.frame(etapa = 1:longitud, distancia = Clus_AG_AV$height)
ggplot(alturas) + aes(x = etapa, y = distancia) +
geom_point() + geom_line() +
scale_x_continuous(breaks = seq(1, longitud, 1000)) +
geom_vline(xintercept = 7597, col = "red", lty = 2) +
geom_text(aes(label = round(distancia,1)),
size = 3, hjust= +1, vjust= -1) +
theme_classic() #+ gghighlight(distancia > 11)
El método grafico nos sugiere la existencia de 2 a 5
agrupaciones.
Determinado el número de clusters optimo
re_process=FALSE
if (file.exists("res.nbclustAV.RData")& !re_process) { #Cambio
load('res.nbclustAV.RData')
}else{
seed = 2022
res.nbclustAV <- NbClust(aya, distance ="euclidean",
min.nc = 2, max.nc = 8, method = "average", index ="all") #Cambio
save(res.nbclustAV,file='res.nbclustAV.RData')
}
par(mfrow=c(1,1))
fviz_nbclust_x(res.nbclustAV)
Among all indices:
===================
* 2 proposed 0 as the best number of clusters
* 1 proposed 1 as the best number of clusters
* 9 proposed 2 as the best number of clusters
* 8 proposed 3 as the best number of clusters
* 2 proposed 7 as the best number of clusters
* 4 proposed 8 as the best number of clusters
Conclusion
=========================
* According to the majority rule, the best number of clusters is 2 .

los diferentes resultados obtenidos al variar todas las
combinaciones la cantidad de clúster deseados para el método de enlace
elegido: average.
Realizando clustering con K=2
# Cortando en 2 cluester
grp_AV=cutree(Clus_AG_AV, k = 2)
# Number de casos en cada cluster
table(grp_AV)
grp_AV
1 2
7218 381
# Descripción de cada cluster
med<-aggregate(aya_bkp, by=list(cluster=grp_AV), mean) #medias
kable(med) %>% kable_styling("striped") %>% scroll_box(width = "100%")
cluster
|
EdadMeses
|
Peso
|
Talla
|
IMC
|
PTZ
|
ZTE
|
ZPE
|
Hemoglobina
|
AlturaREN
|
Hemoglobinaajustada
|
1
|
24.6
|
11.2
|
83.0
|
13.5
|
0.133
|
-1.192
|
-0.507
|
13
|
2841
|
11.5
|
2
|
27.2
|
14.4
|
87.9
|
16.3
|
1.875
|
-0.321
|
1.211
|
13
|
2844
|
11.5
|
#knitr::kable(med, format = "markdown")
Se realiza el corte de la agrupación general (dendograma) en 02
clústeres, tomando como base el resultado óptimo de Nbclust y obtenemos
la cantidad de observaciones por cada uno de ellos. Se puede observar
que el resultado presenta dos grupos, con la mayor cantidad de
individuos agrupados en clúster 01. ## Caracterízando los
cluster
Graficando los clusters.
#fviz_dend(Clus_AG, k = 2, cex = 0.7, horiz = FALSE, k_colors = "jco",
#rect = TRUE, rect_border = "jco", rect_fill = TRUE)
Diagrama de caracterización - lineas
data_plot=scale(aya_bkp) #Scale
#data_plot=apply(aya_bkp, 2, normalize) #Max-Min
M<-as.data.frame(t(rbind(aggregate(data_plot, by=list(cluster=grp_AV), mean)[,-1])))
a=as.vector(colMeans(data_plot))
#fin=data.frame(M,a,names(aya_bkp));names(fin)<-c("Clus1","clus2","clus3","Media","var")
fin=data.frame(M,a,names(aya_bkp));names(fin)<-c("Clus1","clus2","Media","var")
ali=melt(fin,id.vars = "var")
agnes2 <- ggplot(ali, aes(x=var,y=round(value,1),group=variable,colour=variable)) +
geom_point()+ geom_line(aes(lty=variable))+ expand_limits(y = c(-1.9, 1.9))+
theme(axis.text.x = element_text(angle = 60, vjust = 0.5, hjust=1)) +
labs(title="Diagrama de lineas de cluster AGNES por variable cuantitativa con K=2",
x="Variable", y = "valor")
agnes2
El cálculo del promedio por cada grupo no permite diferenciarlos
correctamente con una marcada diferencia en muchas variables.
Diagrama de caracterización - boxplot
dd <- cbind(aya_bkp, cluster =grp_AV )
dd$cluster<-as.factor(dd$cluster)
df.m <- melt(dd, id.var = "cluster")
p <- ggplot(data = df.m, aes(x=variable, y=value)) +
geom_boxplot(aes(fill=cluster))+ facet_wrap( ~ variable, scales="free")
p

Usando el indice de rand para definir el cluster optimo
library(fossil)
rand.index(grp_WR,grp_AV)
[1] 0.39
Segun el indice de Rand, no se podria comprobar que las soluciones
(ward.D y average) clusters son parecidos, por ese motivos elegiremos el
que tiene mayor serparacón a nivel grafico del lineas.
Análisis cluster: K – Means
Determinando número óptimo de clusters
Silhouette method
set.seed(123)
re_process=FALSE
if (file.exists("gg_sil.RData") & !re_process) { #Cambio
load('gg_sil.RData')
}else{
gg_sil=fviz_nbclust(aya, kmeans, method = "silhouette")+
labs(subtitle = "Silhouette method")
save(gg_sil,file='gg_sil.RData')
}
gg_sil

El resultado del índice de silueta establece que para K-means es
óptimo utilizar 3 clústeres, de este modo se generaron 3 clústeres con
2962, 2210 y 2427 respectivamente.
Elbow method (WSS)
set.seed(123)
re_process=FALSE
if (file.exists("gg_WSS.RData") & !re_process) { #Cambio
load('gg_WSS.RData')
}else{
gg_WSS=fviz_nbclust(aya, kmeans, method = "wss") + geom_vline(xintercept = 3, linetype = 2)+
labs(subtitle = "Elbow method")
save(gg_WSS,file='gg_WSS.RData')
}
gg_WSS

Realizando clustering con K=3
km.res <- kmeans(aya, centers=3,nstart = 25)
grp_km=km.res$cluster
table(grp_km)
grp_km
1 2 3
2962 2210 2427
# Descripción de cada cluster
med<-aggregate(aya_bkp, by=list(cluster=grp_km), mean)
kable(med) %>% kable_styling("striped") %>% scroll_box(width = "100%")
cluster
|
EdadMeses
|
Peso
|
Talla
|
IMC
|
PTZ
|
ZTE
|
ZPE
|
Hemoglobina
|
AlturaREN
|
Hemoglobinaajustada
|
1
|
18.6
|
9.77
|
77.6
|
12.6
|
-0.089
|
-1.436
|
-0.765
|
12.6
|
2841
|
11.1
|
2
|
26.7
|
13.19
|
86.9
|
15.1
|
1.089
|
-0.496
|
0.547
|
13.0
|
2769
|
11.6
|
3
|
30.5
|
11.77
|
86.7
|
13.6
|
-0.191
|
-1.390
|
-0.882
|
13.6
|
2909
|
12.0
|
#knitr::kable(med, format = "markdown")
Caracterízando los cluster
Graficando los clusters.
#fviz_cluster(km.res, data = aya,
# palette = "jco",
#ellipse.type = "euclid", # Concentration ellipse
#star.plot = TRUE, # Add segments from centroids to items
#repel = TRUE, # Avoid label overplotting (slow)
#ggtheme = theme_minimal())
Diagrama de caracterización - lineas
data_plot=scale(aya_bkp) #Scale
#data_plot=apply(aya_bkp, 2, normalize) #Max-Min
M<-as.data.frame(t(rbind(aggregate(data_plot, by=list(cluster=grp_km), mean)[,-1])))
a=as.vector(colMeans(data_plot))
fin=data.frame(M,a,names(aya_bkp));names(fin)<-c("Clus1","clus2","clus3","Media","var")
#fin=data.frame(M,a,names(aya_bkp));names(fin)<-c("Clus1","clus2","Media","var")
ali=melt(fin,id.vars = "var")
km3 <- ggplot(ali, aes(x=var,y=round(value,1),group=variable,colour=variable)) +
geom_point()+ geom_line(aes(lty=variable))+ expand_limits(y = c(-1.9, 1.9))+
theme(axis.text.x = element_text(angle = 60, vjust = 0.5, hjust=1)) +
labs(title="Diagrama de lineas de cluster K-means por variable cuantitativa con K=3",
x="Variable", y = "valor")
km3
Con respecto a la caracterización de los clústeres, se desprende del
gráfico que el valor más cercano entre clústeres es la altura, mientras
que el valor más alejado es el peso.
Diagrama de caracterización - boxplot
dd <- cbind(aya_bkp, cluster =grp_km )
dd$cluster<-as.factor(dd$cluster)
df.m <- melt(dd, id.var = "cluster")
p <- ggplot(data = df.m, aes(x=variable, y=value)) +
geom_boxplot(aes(fill=cluster))+ facet_wrap( ~ variable, scales="free")
p
El gráfico de cajas indica que la variable más cercana entre
clústers es la alturaREN, y la más lejana es el IMC.
Para la clusterización utilizando el algoritmo Birch, se hizo uso
de un Threshold igual a 0.42 y un Branching Factor igual a 50.
Análisis cluster Birch (reticulate)
Configurando entorno de desarrollo
library(reticulate)
use_python(path_python)
aya_p = r_to_py(aya) #r.aya
Cargando librerias de python en R
import pandas as pd
from sklearn.preprocessing import StandardScaler
from sklearn.cluster import Birch
Definiendo Birch con: threshold=1, branching_factor=2
df = r.aya_p# Obteniendo objeto R to python
brc = Birch(threshold=0.42,branching_factor=50,n_clusters=3)
brc.fit(df)
Birch(threshold=0.42)
Realizando predicción
data_total = pd.concat([df,pd.DataFrame(brc.predict(df))],axis=1)
data_total.head()
EdadMeses Peso Talla ... AlturaREN Hemoglobinaajustada 0
0 -0.103463 0.338491 -1.171154 ... -1.921536 -0.782780 2
1 -0.794094 0.247707 -0.569719 ... 1.022897 -2.052721 2
2 -1.622850 -1.420439 -1.610039 ... 1.337883 0.584849 1
3 -1.346598 -1.704137 -1.382469 ... -0.168571 -0.685092 1
4 -1.346598 -0.523952 -1.106134 ... 1.022897 -1.857346 2
[5 rows x 11 columns]
Proporciones por cada cluster
data_total.groupby(0)['Peso'].count()
0
0 2788
1 2404
2 2407
Name: Peso, dtype: int64
La proporción para los 3 clústeres es de 2788, 2404 y 2407
respectivamente.
Guardamos el objeto con Cluster Birch
Para luego subirlo al R
data_total.to_excel('birch.xlsx')
Leemos el objeto con Cluster Birch en R
bi <- read_excel("birch.xlsx")
birch <- as.data.frame(bi[,2:12])
names (birch)[11] = "cluster"
birch$cluster <- factor(birch$cluster, levels = c(0,1,2), labels = c(1,2,3))
head(birch)
EdadMeses Peso Talla IMC PTZ ZTE ZPE Hemoglobina AlturaREN
1 -0.103 0.338 -1.171 1.593 2.522 -2.0201 0.848 -1.968 -1.922
2 -0.794 0.248 -0.570 0.899 1.725 0.7099 1.663 -1.296 1.023
3 -1.623 -1.420 -1.610 -1.116 -0.612 -0.5202 -0.649 1.138 1.338
4 -1.347 -1.704 -1.382 -1.782 -1.813 -0.6605 -1.585 -0.541 -0.169
5 -1.347 -0.524 -1.106 0.054 0.654 -0.0131 0.584 -1.128 1.023
6 1.002 1.190 0.909 1.157 0.840 0.2243 0.727 -1.212 -1.922
Hemoglobinaajustada cluster
1 -0.7828 3
2 -2.0527 3
3 0.5848 2
4 -0.6851 2
5 -1.8573 3
6 0.0964 3
Caracterízando los cluster
data_plot=birch[1:10]
cluster=birch$cluster
table(cluster)
cluster
1 2 3
2788 2404 2407
# Descripción de cada cluster
med<-aggregate(aya, by=list(cluster=cluster), mean)
med
cluster EdadMeses Peso Talla IMC PTZ ZTE ZPE Hemoglobina
1 1 0.658 0.7457 0.7675 0.590 0.213 0.346 0.321 0.6561
2 2 -0.618 -0.9573 -0.8110 -0.913 -0.605 -0.546 -0.711 0.0541
3 3 -0.144 0.0924 -0.0789 0.229 0.358 0.144 0.338 -0.8140
AlturaREN Hemoglobinaajustada
1 0.122 0.643
2 0.270 -0.146
3 -0.411 -0.599
# knitr::kable(med) %>% kable_styling("striped") %>% scroll_box(width = "100%")
Diagrama de caracterización - lineas
M<-as.data.frame(t(rbind(aggregate(data_plot, by=list(cluster=cluster), mean)[,-1])))
a=as.vector(colMeans(data_plot))
fin=data.frame(M,a,names(aya_bkp));names(fin)<-c("Clus1","Clus2","Clus3","Media","var")
ali=melt(fin,id.vars = "var")
birch3 <- ggplot(ali, aes(x=var,y=round(value,1),group=variable,colour=variable)) +
geom_point()+ geom_line(aes(lty=variable))+ expand_limits(y = c(-1.9, 1.9))+
theme(axis.text.x = element_text(angle = 60, vjust = 0.5, hjust=1)) +
labs(title="Diagrama de lineas de cluster BIRCH por variable cuantitativa con K=3",
x="Variable", y = "valor")
birch3

No existen diferencias tan marcadas en AlturaREN entre el cluster
1 y 2. En PTZ, ZPE y ZTE tampoco hay diferencias marcadas entre el
cluster 1 y 2. Mientras que diferencias más marcadas entre Hemoglobina,
HemoglobinaAjustada, IMC, Peso y Talla
Diagrama de caracterización - boxplot
dd <- cbind(aya_bkp, cluster =cluster )
dd$cluster<-as.factor(dd$cluster)
df.m <- melt(dd, id.var = "cluster")
p <- ggplot(data = df.m, aes(x=variable, y=value)) +
geom_boxplot(aes(fill=cluster))+ facet_wrap( ~ variable, scales="free")
p

El diagrama de cajas indica que la variable con los valores más
cercanos entre clústers es la AlturaREN, mientras que el más alejado es
la Edad en Meses.
Validando los clusters
Interna (cohesión y separación)
re_process=TRUE
# if (file.exists("df_indexs.RData") & !re_process) { #Cambio
# load('df_indexs.RData')
# }else{
library(clusterSim)
library(clValid)
#Indice de Davies-Bouldin: buscamos el valor mas alto posible
DBkm_ <- index.DB(aya, km.res$cluster, centrotypes = "centroids")$DB #kmeans
DBHc_ <-index.DB(aya, grp_WR, d=dis.Data,centrotypes="centroids")$DB #hclust
DBirch_ <-index.DB(aya, as.numeric(cluster), centrotypes="centroids")$DB #Birch
#Indice de dunn: buscamos el valor mas bajo posible
Dnkm_ <- dunn(Data = aya, clusters = km.res$cluster, distance = NULL)#kmeans
DnHc_ <- dunn(dis.Data, grp_WR) #hclust
DnBirch_ <- dunn(Data = aya, clusters = as.numeric(cluster), distance = NULL)#Birch
tipo_=c('kmeans','hclust','Birch')
davies.bouldin_=c(DBkm_,DBHc_,DBirch_)
dunn_=c(Dnkm_,DnHc_,DnBirch_)
df_indexs=data.frame(tipo_,davies.bouldin_,dunn_)
colnames(df_indexs)=c('Cluster','Davies.Bouldin','Dunn')
save(df_indexs,file='df_indexs.RData')
# }
# df_indexs
# kable(df_indexs) %>% kable_styling("striped") %>% scroll_box(width = "100%")
silhouette<-rbind(
mean(silhouette(as.numeric(km.res$cluster) ,dis.Data)[,3]), #kmeans
mean(silhouette(as.numeric(grp_WR) ,dis.Data)[,3]), #hclust
mean(silhouette(as.numeric(birch$cluster) ,dis.Data)[,3]) #Birch
)
cbind(df_indexs, silhouette)
Cluster Davies.Bouldin Dunn silhouette
1 kmeans 1.74 0.0100 0.184
2 hclust 1.81 0.0287 0.125
3 Birch 2.17 0.0174 0.134
De acuerdo el índice Davies.Bouldin tiene el menor valor con el
algoritmo kmeans, el índice Dunn tiene el mayor valor con el algoritmo
Agnes, y el índice de Silhouette tiene el mayor valor con el algoritmo
k-means; por tanto, al tener el mejor valor en dos índices, se decide
trabajar con los resultados del algoritmo K-means.
Resultados
Clusters por algoritmos y su proporción
Se obtuvieron las siguientes proporción de casos por cluster según la
metodología usada:
Grupo 1 |
Cantidad |
3,361 |
2,962 |
2,788 |
|
% |
44.2% |
39.0% |
36.7% |
Grupo 2 |
Cantidad |
2,919 |
2,210 |
2,404 |
|
% |
38.4% |
29.1% |
31.6% |
Grupo 3 |
Cantidad |
1,319 |
2,427 |
2,407 |
|
% |
17.4% |
31.9% |
31.7% |
Análisis de perfiles
Proporción de menores con anemia:
ayacucho_final<-ayacucho
ayacucho_final$Anemia<-if_else(ayacucho$Hemoglobinaajustada*10>=110, 'Sin anemia',
if_else(ayacucho$Hemoglobinaajustada*10>=100,'Leve',
if_else(ayacucho$Hemoglobinaajustada*10>=70,'Moderada',
'Grave')))
ayacucho_final=cbind(ayacucho_final, cluster=km.res$cluster)
table(ayacucho_final$Anemia)
Grave Leve Moderada Sin anemia
11 1186 645 5757
Análisis de perfiles con cluster Agnes
agnes3

En cuanto a las variables numéricas la gráfica muestra el siguiente
patrón: - Cluster 1: conformado por los máximos valores de la variable
IMC, peso, PTZ, Talla, ZPE, ZTE; y con valores cercanos al promedio
general para las variables altura REN, edad en meses y hemoglobina.
Sintetizando: son los menores de edad media, de mayor peso y talla, de
zonas de altura por poco debajo del promedio y con hemoglobina cercana
al promedio general. - Cluster 2: conformado por mínimos valores de la
variable edad, hemoglobina, hemoglobina ajustada, IMC, Peso, Talla, y
con valores cercanos al promedio general para las variables PTZ,ZPE y
ZTE. Sintetizando: son los menores de menor edad, menor peso, talla y
hemoglobina, de zonas de altura promedio. - Cluster 3: conformado por
los máximos valores de la variable AlturaREN, edad, hemoglobina,
hemoglobina ajustada; con valores cercanos al promedio general para las
variables IMC y peso, pero con valores mínimos para las variables PTZ,
ZPE, ZTE, y con talla cercana al valor máximo. Sintetizando: son los
menores de mayor edad (alrededor de 3 años), mayor talla y hemoglobina,
aunque con peso promedio y de zonas de mayor altura.
Análisis de perfiles con cluster K-means
km3

En cuanto a las variables numéricas la gráfica muestra el siguiente
patrón: - Cluster 1: conformado por mínimos valores de la variable edad,
hemoglobina, hemoglobina ajustada, IMC, Peso, Talla, y con valores
cercanos al promedio general para las variables PTZ, ZPE y ZTE.
Sintetizando: son los menores de menor edad (alrededor de un año), menor
peso, talla y hemoglobina, de zonas de altura promedio. - Cluster 2:
conformado por los máximos valores de la variable IMC, peso, PTZ, Talla,
ZPE, ZTE; y con valores cercanos al promedio general para las variables
altura REN, edad en meses, hemoglobina y hemoglobina ajustada.
Sintetizando: son los menores de edad cercana al promedio, de mayor peso
y talla, de zonas de menor altura, aunque muy cercana al promedio, y con
hemoglobina promedio. - Cluster 3: conformado por los máximos valores de
la variable AlturaREN, edad, hemoglobina, hemoglobina ajustada y talla;
con valores cercanos al promedio general para las variables IMC y peso,
pero con valores mínimos para las variables PTZ, ZPE y ZTE.
Sintetizando: son los menores de mayor edad, mayor talla, aunque con
peso promedio y de zonas de mayor altura y mayor hemoglobina.
Análisis de perfiles con cluster BIRCH
birch3

En cuanto a las variables numéricas la gráfica muestra el siguiente
patrón: - Cluster 1: conformado por maximos valores de la variable edad,
hemoglobina, hemoglobina ajustada, IMC, Peso, Talla, ZPE, ZTE, y con
valores cercanos al promedio general para las variables AlturaREN y PTZ.
Sintetizando: son los menores de mayor edad, de mayor peso, talla y
hemoglobina, de zonas de altura promedio. - Cluster 2: conformado por
los mínimos valores de la variable edad, IMC, peso, PTZ, Talla, ZPE,
ZTE; y con valores cercanos al promedio general para la variables
hemoglobina y hemoglobina ajustada; aunque de mayor alturaREN.
Sintetizando: son los menores de menor edad, de menor peso, talla, de
zonas de mayor altura y hemoglobina promedio. - Cluster 3: conformado
por los menores valores en la variable alturaREN, hemoglobina y
hemoglobina ajustada; pero con valores que fluctuan alrededor del
promedio en las variables edad, IMC, peso, talla y ZTE; con valores
máximos en PTZ y ZPE. Sintetizando: son los menores de edad promedio,
con medidas antropométricas promedio, de menor hemoblobina y
alturaREN.
Análisis de perfiles con cluster K-means según hemoglobina
Tomando los resultados de K-means se analiza la variable
hemoglobina
g1_F <-
ggplot(mutate(ayacucho_final, cluster = factor(cluster))) +
aes(cluster, fill =Anemia ) +
geom_bar(cluster = position_fill()) +
labs(title="Gráfica de Cluster K-means según Anemia",
x = NULL, y = "Proporción") +
theme_bw()
g1_F

La gráfica muestra el siguiente patrón en cuanto la anemia: - Cluster
1: Conformado por los menores que tienen anemia en mayor proporción,
casi el 50% del cluster. - Cluster 2: Conformado por los menores que
tiene anemia en baja proporción pero mayor a la del cluster 3. - Cluster
3: Conformado por los menores que tiene anemia en la más baja
proporción.
g1_S <-
ggplot(mutate(ayacucho_final, cluster = factor(cluster))) +
aes(cluster, fill =Sexo ) +
geom_bar(cluster = position_fill()) +
labs(title="Gráfica de Cluster K-means según Sexo",
x = NULL, y = "Proporción") +
theme_bw()
g1_S
A nivel de género del menor, se observa un equilibrio entre los
clústers.
Conclusiones
La evaluación con la función NbClust nos indica que debemos
trabajar con 3 grupos.
LA evaluación de la clusterización permite concluir que el
perfilado del algoritmo Kmeans es el más adecuado en comparación con los
otros algorimos usados.
Kmeans |
1.74 |
0.0100 |
0.184 |
Agnes |
1.81 |
0.0287 |
0.125 |
Birch |
2.17 |
0.0174 |
0.134 |
Finalmente se obtuvieron los siguientes clusters con el algoritmo
Kmeans: En cuanto a las variables numéricas la gráfica muestra el
siguiente patrón:
- Cluster 1: conformado por mínimos valores de la variable edad,
hemoglobina, hemoglobina ajustada, IMC, Peso, Talla, y con valores
cercanos al promedio general para las variables PTZ, ZPE y ZTE.
Sintetizando: son los menores de menor edad (alrededor de un año), menor
peso, talla y hemoglobina, de zonas de altura promedio. Además, tienen
anemia en mayor proporción, casi el 50% del cluster.
- Cluster 2: conformado por los máximos valores de la variable IMC,
peso, PTZ, Talla, ZPE, ZTE; y con valores cercanos al promedio general
para las variables altura REN, edad en meses, hemoglobina y hemoglobina
ajustada. Sintetizando: son los menores de edad cercana al promedio, de
mayor peso y talla, de zonas de menor altura, aunque muy cercana al
promedio, y con hemoglobina promedio. También, tienen anemia en baja
proporción pero mayor a la del cluster 3.
- Cluster 3: conformado por los máximos valores de la variable
AlturaREN, edad, hemoglobina, hemoglobina ajustada y talla; con valores
cercanos al promedio general para las variables IMC y peso, pero con
valores mínimos para las variables PTZ, ZPE y ZTE. Sintetizando: son los
menores de mayor edad, mayor talla, aunque con peso promedio y de zonas
de mayor altura y mayor hemoglobina. Igualmente, tienen anemia en la más
baja proporción
LS0tDQp0aXRsZTogIkFsZ29yaW1vIEJpcnRjaDogRXN0YWRvIG51dHJpY2lvbmFsIGRlIG5pw7FhcyB5IG5pw7FvcyBtZW5vcmVzIGRlIDEgYSAzIGHDsW9zDQogIGRlIGxhIFJlZ2nDs24gZGUgQXlhY3VjaG8gLSBQZXLDuiINCmRhdGU6ICIyMDIyLTEyLTAzIg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIHRvYzogeWVzDQogICAgdG9jX2Zsb2F0Og0KICAgICAgY29sbGFwc2VkOiBubw0KICAgICAgc21vb3RoX3Njcm9sbDogbm8NCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIGFuY2hvcl9zZWN0aW9uczogeWVzDQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQogICAgY29kZV9kb3dubG9hZDogeWVzDQogICAgdGhlbWU6IHlldGkNCiAgd29yZF9kb2N1bWVudDoNCiAgICB0b2M6IHllcw0KLS0tDQo8IS0tIFBhcmFtZXRyb3MgZ2VuZXJhbGVzIGRlbCBtYXJrZG9udy0tPg0KDQpgYGB7ciBpbmNsdWRlPUZBTFNFfQ0KI0NvbmZpZ3VyYWNpw7NuIGRlIGxhcyBjaHVuaw0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFLGNvbW1lbnQgPSBOQSkNCmBgYA0KDQojICoqSW50ZWdyYW50ZXMqKg0KMS4gVmljdG9yIERpYXogT2xpdm8NCjIuIE1pbGxlciBTYXJzb3phIEFsYmlubw0KMy4gRGF2aWQgSHVhbcOhbiBQYXNhcGVyYQ0KNC4gQ2FybG9zIEVkdWFyZG8gTGF6byBIZXJyZXJhDQoNCg0KIyAqKlJlc3VtZW4qKg0KDQpFbCBwcmVzZW50ZSBwcm95ZWN0byBldmFsw7phIHZhcmlhYmxlcyByZWxhY2lvbmFkYXMgYSBuacOxYXMgeSBuacOxb3MgbWVub3JlcyBkZSAxIGEgMyBhw7FvcyBkZSBsYSBSZWdpw7NuIGRlIEF5YWN1Y2hvLCBxdWUgZnVlcm9uIGF0ZW5kaWRvcyBlbiBsb3MgZXN0YWJsZWNpbWllbnRvcyBkZSBzYWx1ZCBkZSBEaXJlY2Npw7NuIGRlIFJlZ2lvbmFsIGRlIFNhbHVkIChEaXJlc2EpIHJlc3BlY3RpdmEsIGVuIGVsIGHDsW8gMjAyMSwgY29uIGVsIG9iamV0byBkZSBvYnRlbmVyIHBlcmZpbGVzIG51dHJpY2lvbmFsZXMgcXVlIHBlcm1pdGFuIGZvY2FsaXphciBlbCBzZXJ2aWNpbyBkZSBsb3MgY2VudHJvcyBkZSBzYWx1ZCByZXNwZWN0aXZvcy4gU2UgYXBsaWNhcm9uIHRyZXMgKDMpIG1ldG9kb2xvZ8OtYXMgZGUgYWdydXBhbWllbnRvOiBDbHVzdGVyIEplcmFycXVpY28gQWduZXMsIGNsdXN0ZXIgYmFzYWRvIGVuIHBhcnRpY2lvbmVzIEstbWVhbnMgeSBhbGdvcml0bW8gQklSQ0gsIG9idGVuaWVuZG8gcXVlIGxhIG1lam9yIGFncnVwYWNpw7NuIGVzdMOhIGRhZGEgcG9yIGVsIHNlZ3VuZG8gYWxnb3JpdG1vLg0KDQoNCiMgKipJbnRyb2R1Y2Npw7NuKioNCg0KTGEgZGVzbnV0cmljacOzbiBjcsOzbmljYSBlbiBsYSBpbmZhbmNpYSBlcyB1bm8gZGUgbG9zIHByaW5jaXBhbGVzIHByb2JsZW1hcyBkZSBzYWx1ZCBww7pibGljYSB5IHVuIGluZGljYWRvciBkZSBkZXNhcnJvbGxvIHNvY2lhbCBkZWwgcGHDrXM7IHRpZW5lIGVmZWN0b3MgaXJyZXZlcnNpYmxlcyBlbiBlbCBkZXNhcnJvbGxvIGRlIGhhYmlsaWRhZGVzIHkgY2FwYWNpZGFkZXMgZW4gbGEgbmnDsWEgeSBlbCBuacOxby4NCg0KRWwgSU5FSSBlbiBlbCBpbmZvcm1lIOKAnFBlcsO6OiBJbmRpY2Fkb3JlcyBkZSByZXN1bHRhZG9zIGRlIGxvcyBwcm9ncmFtYXMgcHJlc3VwdWVzdGFsZXMsIDIwMTUtMjAyMOKAnSBzZcOxYWxhIHF1ZSBlbCAxMi4xJSBkZSBsYSBwb2JsYWNpw7NuIG1lbm9yIGRlIGNpbmNvIGHDsW9zIGRlIGVkYWQgZGVsIHBhw61zIHN1ZnJpw7MgZGVzbnV0cmljacOzbiBjcsOzbmljYSBlbCBhw7FvIDIwMjAsIHNpZW5kbyAwLjElIG1lbm9yIHF1ZSBlbCAyMDE5IHkgLTIuMyUgcmVzcGVjdG8gYWwgMjAxNSwgb2JzZXJ2w6FuZG9zZSBxdWUgbm8gaHVibyBhdmFuY2VzIHNpZ25pZmljYXRpdm9zLiBBIHN1IHZleiwgZWwgbWlzbW8gaW5mb3JtZSBzZcOxYWxhIHF1ZSBlbCA0MCUgZGUgbmnDsW9zIHkgbmnDsWFzIGVudHJlIDYgYSAzNSBtZXNlcyBkZWwgbWlzbW8gYcOxbyBwcmVzZW50YXJvbiBwcmV2YWxlbmNpYSBkZSBhbmVtaWFbMV0uDQoNCkVsIGRpc2XDsW8gZGUgcG9sw610aWNhcyBww7pibGljYXMsIHkgZW4gZXNwZWPDrWZpY28sIGVsIGRlc2Fycm9sbG8gZGUgcHJvZ3JhbWFzIHkgcHJveWVjdG9zIHNvY2lhbGVzLCBwYXJhIG1lam9yYXIgZWwgZXN0YWRvIG51dHJpY2lvbmFsIGRlIGxvcyBtZW5vcmVzIGRlIGNpbmNvIGHDsW9zIGVzIHVuYSBwcmlvcmlkYWQgcGFyYSBlbCBkZXNhcnJvbGxvIG5hY2lvbmFsOyBzaWVuZG8gaW1wb3J0YW50ZSBsYSBnZW5lcmFjacOzbiBkZSBpbnZlc3RpZ2FjaW9uZXMgcXVlIHBlcm1pdGFuIGNvbm9jZXIgc3VzIHBlcmZpbGVzIG51dHJpY2lvbmFsZXMuIEVuIGVzdGUgbWFyY28sIGVsIGFuw6FsaXNpcyBlc3TDoWRpc3RpY28gZGUgZGF0b3MgdGllbmUgbXVjaG8gcG9yIGNvbnRyaWJ1aXIuDQoNCkVuIGVzZSBzZW50aWRvIGVsIHByZXNlbnRlIHRyYWJham8gdGllbmUgY29tbyBvYmpldGl2byBsYSBvYnRlbmNpw7NuIGRlIGNsw7pzdGVyZXMgcGFyYSBjb25vY2VyIGxvcyBwZXJmaWxlcyBudXRyaWNpb25hbGVzIGRlIG5pw7FvcyB5IG5pw7FhcyBtZW5vcmVzIGRlIDEgYSAzIGHDsW9zIHF1ZSBmdWVyb24gYXRlbmRpZG9zIGVuIGVzdGFibGVjaW1pZW50b3MgZGUgc2FsdWQgZGUgcHJpbWVyIG5pdmVsIGRlIGxhIERpcmVzYSBBeWFjdWNobyBlbCBhw7FvIDIwMjEsIGNvbiBmaW5lcyBkZSBpZGVudGlmaWNhciBwYXRyb25lcyBxdWUgcGVybWl0YW4gbGEgcGxhbmlmaWNhY2nDs24gZGUgaW50ZXJ2ZW5jaW9uZXMgZm9jYWxpemFkYXMgcXVlIGNvbnRyaWJ1eWFuIGEgbWVqb3JhciBzdSBjYWxpZGFkIGRlIHZpZGEuDQoNCiMgKipEZXNjcmlwY2nDs24gZGUgbG9zIGRhdG9zKioNCg0KTG9zIGRhdG9zIGNvcnJlc3BvbmRlbiBhIGluZm9ybWFjacOzbiBkZSBoaXN0b3JpYXMgY2zDrW5pY2FzIGRlIGxvcyBlc3RhYmxlY2ltaWVudG9zIGRlIHNhbHVkIGRlbCBNaW5pc3RlcmlvIGRlIFNhbHVkIGRlbCBQZXLDuiAoTWluc2EpLCB5IHF1ZSBlcyByZWdpc3RyYWRhIGVuIGVsIFNpc3RlbWEgZGUgSW5mb3JtYWNpw7NuIGRlbCBFc3RhZG8gTnV0cmljaW9uYWwgKFNJRU4pLCBhZG1pbmlzdHJhZG8gcG9yIGVsIENlbnRybyBOYWNpb25hbCBkZSBBbGltZW50YWNpw7NuIHkgTnV0cmljacOzbiAoQ0VOQU4pLCDDs3JnYW5vIGRlIGzDrW5lYSB0w6ljbmljbyBub3JtYXRpdm8gZGVsIEluc3RpdHV0byBOYWNpb25hbCBkZSBTYWx1ZCAoSU5TKS4gTGEgYmFzZSBkZSBkYXRvcyBxdWUgdXNhbW9zIGN1ZW50YSBjb24gdW4gdG90YWwgZGUgIiI3LDU5OSIiIHJlZ2lzdHJvcyBkZSBtZW5vcmVzIGRlIDEgYSAzIGHDsW9zIHkgZnVlIHRvbWFkYSBkZSBsYSBQbGF0YWZvcm1hIE5hY2lvbmFsIGRlIERhdG9zIEFiaWVydG9zWzJdLg0KDQpbRnVlbnRlXShodHRwczovL2RhdG9zLmlucy5nb2IucGUvZGF0YXNldC9zaXN0ZW1hLWRlLWluZm9ybWFjaW9uLWRlbC1lc3RhZG8tbnV0cmljaW9uYWwtZGUtbmlub3MteS1nZXN0YW50ZXMtcGVydS1pbnMtY2VuYW4tMjAxOS0yMDIwKQ0KDQoNCioqVW5pZGFkIGRlIGFuw6FsaXNpcyoqDQoNCk5pw7FhcyB5IG5pw7FvcyBtZW5vcmVzIGRlIDEgYSAzIGHDsW9zIHF1ZSBmdWVyb24gYXRlbmRpZG9zIGVuIGxvcyBlc3RhYmxlY2ltaWVudG9zIGRlIHNhbHVkIGRlbCBwcmltZXIgbml2ZWwgZGUgYXRlbmNpw7NuIGRlIGxhIERpcmVzYSBBeWFjdWNobyBlbCBhw7FvIDIwMjEuDQoNCioqVmFyaWFibGVzKioNCg0KTGFzIHZhcmlhYmxlcyB1dGlsaXphZGFzIGVuIGVsIGFuw6FsaXNpcyBzb24gbGFzIHNpZ3VpZW50ZXM6DQoNCl9WYXJpYWJsZXMgcHJlZGljdG9yYXM6Xw0KKkVkYWQvbWVzZXM6IEVkYWQgZW4gbWVzZXMgZGVsIG1lbm9yIGF0ZW5kaWRvIChkZSAxMiBhIDM2IG1lc2VzKS4NCipQZXNvIChrZykuDQoqVGFsbGEgKGNtKS4NCipQVFo6IMONbmRpY2UgcXVlIGNvbXBhcmEgZWwgcGVzbyBkZWwgbWVub3IgY29uIGVsIHBlc28gZXNwZXJhZG8gcGFyYSBzdSB0YWxsYSB5IHBlcm1pdGUgZXN0YWJsZWNlciBzaSBoYSBvY3VycmlkbyB1bmEgcMOpcmRpZGEvZ2FuYW5jaWEgZGUgcGVzbyBjb3Jwb3JhbC4NCipaVEU6IMONbmRpY2UgcXVlIGNvbXBhcmEgbGEgdGFsbGEgZGVsIG1lbm9yIGNvbiBsYSB0YWxsYSBlc3BlcmFkYSBwYXJhIHN1IGVkYWQgeSBwZXJtaXRlIGVzdGFibGVjZXIgc2kgZXN0w6Egb2N1cnJpZW5kbyB1biByZXRyYXNvIGVuIGVsIGNyZWNpbWllbnRvLg0KKlpQRTogw41uZGljZSBxdWUgY29tcGFyYSBlbCBwZXNvIGRlbCBtZW5vciBjb24gZWwgcGVzbyBlc3BlcmFkbyBwYXJhIHN1IGVkYWQgeSBwZXJtaXRlIGVzdGFibGVjZXIgc2kgZXN0w6Egb2N1cnJpZW5kbyBkZXNudXRyaWNpw7NuLg0KKklNQzogw41uZGljZSBkZSBtYXNhIGNvcnBvcmFsLg0KKkhlbW9nbG9iaW5hIChnL2RMKQ0KKkhlbW9nbG9iaW5hIGFqdXN0YWRhOiBIZW1vZ2xvYmluYSBhanVzdGFkYSBzZWfDum4gbGEgYWx0dXJhIGRlIGxhIGxvY2FsaWRhZCBkZSByZXNpZGVuY2lhIGRlbCBtZW5vci4NCipBbHR1cmFSRU46IE1ldHJvcyBzb2JyZSBlbCBuaXZlbCBkZWwgbWFyIGRlIGxhIGxvY2FsaWRhZCBkZSByZXNpZGVuY2lhIGRlbCBtZW5vci4NCg0KQWRpY2lvbmFsbWVudGUgc2UgdXRpbGl6w7MgbGEgdmFyaWFibGUgU2V4byBkZWwgbWVub3IgeSBBbmVtaWEgcGFyYSBlbCBhbsOhbGlzaXMgZGVsIHBlcmZpbGFkbyBvYnRlbmlkby4NCg0KRGViZW1vcyBzZcOxYWxhciBxdWUgbGEgdmFyaWFibGUgYW5lbWlhIHNlIGNhbGN1bMOzIGEgcGFydGlyIGRlIGxhIHZhcmlhYmxlIGhlbW9nbG9iaW5hIGFqdXN0YWRhLCBxdWUgYSBzdSB2ZXogZnVlIGNhbGN1bGFkYSBkZSBsYSB2YXJpYWJsZSBoZW1vZ2xvYmluYSBzaWd1aWVuZG8gbGFzIHJlY29tZW5kYWNpb25lcyBkZWwgTWluaXN0ZXJpbyBkZSBTYWx1ZCAtIFBlcsO6IHBhcmEgY29uY2VudHJhY2lvbmVzIGRlIGhlbW9nbG9iaW5hIGVuIGZ1bmNpw7NuIGRlIGxhIGFsdGl0dWQgc29icmUgZWwgbml2ZWwgZGVsIG1hciwgaW5jb3Jwb3JhbmRvIHVuIGZhY3RvciBkZSBhanVzdGUgcG9yIGFsdGl0dWRbM10uIEx1Z28gc2UgcmVhbGl6w7MgZWwgY8OhbGN1bG8gZGUgbGEgdmFyaWFibGUgQW5lbWlhIGVuIGJhc2UgYSBsYSB2YXJpYWJsZSBIZW1vZ2xvYmluYSBhanVzdGFkYSBwYXJhIGFuYWxpemFyIGxvcyByZXN1bHRhZG9zIGRlbCBhbGdvcml0bW8gSy1tZWFucyBwb3IgY2x1c3RlcnMsIHVzYW5kbyB2YWxvcmVzIGRlIGNvcnRlIGNvbiBiYXNlIGVuIGxhcyBjb25jZW50cmFjaW9uZXMgZGUgaGVtb2dsb2JpbmEgcGFyYSBkaWFnbm9zdGljYXIgYW5lbWlhIGFsIG5pdmVsIGRlbCBtYXIgKGcvbCnCsSBxdWUgcmVjb21pZW5kYSBsYSBPTVNbNF0uDQoNCg0KIyAqKk1ldG9kb2xvZ8OtYSoqDQoNCg0KRWwgbm9tYnJlIEJJUkNIIHZpZW5lIGRlIEJhbGFuY2VkIEl0ZXJhdGl2ZSBSZWR1Y2luZyBhbmQgQ2x1c3RlcmluZyBVc2luZyBIaWVyYXJjaGllcywgZXMgdW4gbcOpdG9kbyBkZSBhZ3J1cGFtaWVudG8gamVyw6FycXVpY28gcXVlIHRyYWJhamEgbXV5IGJpZW4gY29uIHNldHMgZGUgZGF0b3MgbWFzaXZvcyBkYWRvIHF1ZSBhZ3J1cGEgZGUgZm9ybWEgaW5jcmVtZW50YWwgeSBkaW7DoW1pY2EgbG9zIGRhdG9zIGVudHJhbnRlcyBwYXJhIGRhciBlbCBtZWpvciBhZ3J1cGFtaWVudG8gY29uIGxvcyByZWN1cnNvcyBkaXNwb25pYmxlcywgZGVsIG1pc21vIG1vZG8gdW5hIGNhcmFjdGVyw61zdGljYSBwcmluY2lwYWwgZGVsIGFsZ29yaXRtbyBlcyBxdWUgZXNjYW5lYSBsb3MgZGF0b3MgdW5hIHNvbGEgdmV6WzVdLiANCg0KTG9zIGNvbmNlcHRvcyBtw6FzIHJlbGV2YW50ZXMgY29uIHJlc3BlY3RvIGFsIGFsZ29yaXRtbyBzb24gbG9zIHNpZ3VpZW50ZXM6IA0KDQogIC0JMS4gQ2x1c3RlcmluZyBGZWF0dXJlOiANCiAgICBTaSBzZSB0aWVuZSB1biBzZXQgZGUgTiBkYXRvcywgZWwgY2x1c3RlcmluZyBmZWF0dXJlIHNlIGRlZmluZSBjb21vOiANCg0KICAkQ0Y9KE4sXGJhcntMU30sU1MpJA0KDQpEb25kZToNCg0KICAkXGJhcntMU309XHN1bV97aT0wfV5uXGJhcntYX2l9JA0KDQogICRTUz1cc3VtX3tpPTB9Xm5cYmFye1hfaX1eMiQNCg0KDQoqIFNpIHNlIHRpZW5lbiBsb3MgZGF0b3MgJCh4XzEpOyh4XzIpOyh4XzMpOyh4XzQpJA0KDQogIC0JRWwgdmFsb3IgTiBzZXLDoSBpZ3VhbCBhIDQNCiAgDQogIC0JRWwgdmFsb3IgZGUgJFxiYXJ7TFN9JCBlcyBpZ3VhbCBhICAkKHhfMSt4XzIreF8zK3hfNCkkDQogIA0KICAtIEVsIHZhbG9yIGRlICRTUyQgc2Vyw6EgaWd1YWwgYSAgJCh4XzFeMit4XzJeMit4XzNeMit4XzReMikkDQogIA0KICAtCURlIGVzdGEgZm9ybWEgYWwgZmluYWwgbG9zIHB1bnRvcyBkZSBtdWVzdHJhIGVzdMOhbiByZXByZXNlbnRhZG9zIGNvbW8gJENGPShOLFxiYXJ7TFN9LFNTKSQNCg0KDQotCTIuIENsdXN0ZXJpbmcgRmVhdHVyZSBUcmVlIChDRlQpOiBDb24gbG9zIGRhdG9zIGdlbmVyYWRvcyBwb3IgZWwgQ2x1c3RlcmluZyBGZWF0dXJlIGRlIGNvbnN0cnV5ZSB1biDDoXJib2wgZGUgY2zDunN0ZXJzIGVuIG9yZGVuIGplcsOhcnF1aWNvIGRlIHRhbCBtb2RvIHF1ZSBsYXMgaG9qYXMgcHJldmlhcyByZXByZXNlbnRhbiBjbMO6c3RlcnMgZGUgbWF5b3IgdGFtYcOxbywgeSBxdWUgbGEgZGl2aXNpw7NuIGRlIGVzdG9zIHByb2R1emNhbiBhIHN1IHZleiBudWV2b3Mgbm9kb3MgKGNsw7pzdGVycykgZGUgbWVub3IgdGFtYcOxby4NCg0KKglFbCBwcm9jZWRpbWllbnRvIGN1bG1pbmEgdW5hIHZleiBxdWUgbG9zIGNsw7pzdGVycyBhbGNhbmNlbiB1biB0YW1hw7FvIGluZmVyaW9yIGEgdW4gcGFyw6FtZXRybyBkYWRvIG8gc2UgbG9ncmUgZWwgbsO6bWVybyBkZSBjbMO6c3RlcnMgZGVzZWFkb1s2XS4gIA0KDQoqIEVsIGFsZ29yaXRtbyBCaXJjaCBjdWVudGEgY29uIGRvcyBwcmluY2lwYWxlcyBwYXLDoW1ldHJvcw0KICANCiAgLSBpKSBCcmFuY2hpbmcgZmFjdG9yIChCKTogQ2FudGlkYWQgbcOheGltYSBkZSBzdWJjbMO6c3RlcnMgZW4gY2FkYSBub2RvLCBlbiBjYXNvIGRlICAgICAgICAgICBzdXBlcmFyIGVzdGUgZmFjdG9yIGVsIG5vZG8gc2UgZGl2aWRlIGVuIGRvcyBub2RvcyBjb24gbG9zIHN1YmNsw7pzdGVycyAgICAgICAgICAgICAgICAgIHJlZGlzdHJpYnVpZG9zLiANCiAgDQogIC0JaWkpIFRocmVzaG9sZCAoVCk6IEVzIGVsIHVtYnJhbCBlc3RhYmxlY2lkbyBwYXJhIGxhIGNyZWFjacOzbiBkZSB1biBudWV2byBzdWJjbMO6c3Rlci4gRXN0YWJsZWNlciBlc3RlIHZhbG9yIGJham8gZ2VuZXJhIGxhIGNyZWFjacOzbiBkZSBtw6FzIGNsw7pzdGVycy4NCg0KICANCioqRWplbXBsbyBkZSBhcGxpY2FjacOzbiBkZWwgQWxnb3JpdG1vKioNCg0KQ29uc2lkZXJhbmRvIGxvcyBzaWd1aWVudGVzIHZhbG9yZXM6DQoNCg0KICB8IE4JfCBQdW50b3MgfA0KICB8Oi0tOnw6LS0tLTp8DQogIHwgMQl8IDIyIHwNCiAgfCAyCXwgOSB8DQogIHwgMwl8IDEyIHwNCiAgfCA0CXwgMTUgfA0KDQoNCioqUGFzbyAxOioqIFNlIGVsaWdlIGVsIFRyZXNob2xkICg1KS4NCg0KKipQYXNvIDI6KiogU2UgZWxpZ2UgZWwgQnJhbmNoaW5nIGZhY3RvciAoMykuDQoNCioqUGFzbyAzOioqIFNlIHRvbWEgZWwgcHJpbWVyIHZhbG9yIHkgc2UgZWxldmEgYWwgY3VhZHJhZG8uDQoNCioqUGFzbyA0OioqIFNlIGVsZXZhIGFsIGN1YWRyYWRvIGFtYm9zIHZhbG9yZXMgeSBzZSBzdW1hLg0KDQoNCiAgfFZhbG9yZXMgfAlOCXwgU3VtYQl8IEN1YWRyYWRvDQogIHw6LS06fDotLTp8Oi0tOnw6LS06fA0KICB8MjIJfDEJfDIyfAk0ODR8DQogIHw5CXwyCXwzMXwJNTY1fA0KDQoNCioqUGFzbyA1OioqIEVsIGNlbnRyb2lkZSBlcyBpZ3VhbCBhIGxhIHN1bWEgZGUgbG9zIHZhbG9yZXMgc29icmUgTiwgcGFyYSBlbCBwcmltZXIgdmFsb3IgZWwgY2VudHJvaWRlIHNlcsOtYToNCiAgDQogICRDZW50cm9pZGVfMCA9IDIyLzEgPSAyMiQNCiAgDQogIA0KKipQYXNvIDY6KiogU2UgYXBsaWNhIGxhIGbDs3JtdWxhDQogIA0KJEQ9XHNxcnR7XGZyYWN7XHN1bV97aT0xfV5uXHN1bV97aj0xfV5uKHhfaS14X2opXjJ9e24obi0xKX19JA0KDQokRD1cc3FydHtcZnJhY3sybihTUyktMihMUyleMn17bihuLTEpfX0kDQoNCiREPVxzcXJ0e1xmcmFjezIqMig1NjUpLTIoMzEpXjJ9ezIoMi0xKX19JA0KICANCkVudG9uY2VzICREPTEzJCwgcXVlIGVzIHN1cGVyaW9yIGFsIHRyZXNob2xkLCBwb3IgbG8gdGFudG8sIHNlIGNyZWEgdW4gbnVldm8gY2zDunN0ZXIuDQogIA0KICANCioqUGFzbyA3OioqIFRyYWJhamFuZG8gZWwgc2lndWllbnRlIHZhbG9yOiAxMg0KICANCiAgJENlbnRyb2lkZV8wID0gMjIvMSA9IDIyJA0KICANCiAgJENlbnRyb2lkZV8xID0gOS8xID0gOSQNCiAgDQogIEVsIHB1bnRvIDEyIGVzdGEgbcOhcyBjZXJjYSBhIDkuDQogIA0KICB8VmFsb3JlcyB8CU4JfCBTdW1hCXwgQ3VhZHJhZG8NCiAgfDotLTp8Oi0tOnw6LS06fDotLTp8DQogIHwgOSB8IDEJfCA5CXwgODF8DQogIHwgMTIJfCAyCXwyMQl8IDIyNXwNCiANCiAgJEQ9XHNxcnR7XGZyYWN7MioyKDIyNSktMigyMSleMn17MigyLTEpfX0kDQogIA0KICAkRD0zJA0KICANCiAgQ29tbyAkRD0zPDUkLCBzZSBqdW50YW4gbG9zIHB1bnRvcyA5IHkgMTIuDQogIA0KDQoqKlBhc28gODoqKiBUcmFiYWphbmRvIGVsIHNpZ3VpZW50ZSB2YWxvcjogMTUNCiAgDQogICRDZW50cm9pZGVfMiA9ICg5KzEyKS8yID0gMTAuNSQNCiAgDQogICRDZW50cm9pZGVfMCA9ICgyMikvMSA9IDIyJA0KICANCiAgRWwgcHVudG8gMTIgZXN0YSBtw6FzIGNlcmNhIGFsICRDZW50cm9pZGVfMj0xMC41JC4NCiAgDQogIHxWYWxvcmVzIHwJTgl8IFN1bWEJfCBDdWFkcmFkbw0KICB8Oi0tOnw6LS06fDotLTp8Oi0tOnwNCiAgfCA5LCAxMgl8IDIJfDIxCXwgMjI1fA0KICB8IDksIDEyLCAxNQl8IDMJfCAzNgl8IDQ1MHwNCiANCiAgJEQ9XHNxcnR7XGZyYWN7MiozKDQ1MCktMigzNileMn17MigyLTEpfX0kDQogIA0KICAkRD00LjI0JA0KICANCiAgQ29tbyAkRD00LjI0PDUkLCBzZSBqdW50YW4gbG9zIHB1bnRvcyA5LCAxMiB5IDE1Lg0KICANCiAgJENlbnRyb2lkZV8zID0gKDkrMTIrMTUpLzMgPSAxMiQNCiAgDQogICRDZW50cm9pZGVfMCA9ICgyMikvMSA9IDIyJA0KICAgIA0KKipFamVtcGxvIGVuIFB5dGhvbioqDQoNCmBgYHtyfQ0KI0NvbmZpZ3VyYWNpw7NuIGRlbCBlbnRvcm5vDQpybShsaXN0ID0gbHMoKSkNCnNldHdkKGRpcm5hbWUocnN0dWRpb2FwaTo6Z2V0QWN0aXZlRG9jdW1lbnRDb250ZXh0KCkkcGF0aCkpDQpncmFwaGljcy5vZmYoKQ0Kb3B0aW9ucyhzY2lwZW4gPSA5OTkpICANCm9wdGlvbnMoZGlnaXRzID0gMykgDQpwYXRoX3B5dGhvbj0iQzovVXNlcnMvQVNVUy9hbmFjb25kYTMiDQojQ2FyZ2FuZG8gcGFxdWV0ZXMgbmVjZXNhcmlvcw0KbGlicmFyeShwYWNtYW4pDQpwX2xvYWQocmV0aWN1bGF0ZSwgUGVyZm9ybWFuY2VBbmFseXRpY3MsIHB1cnJyLCBza2ltciwgY29ycnBsb3QsIGNsdXN0ZXIsIHBzeWNoLCBnZ3Bsb3QyLCANCiAgICAgICBzdHJlYW0sIGVsbGlwc2UsIHRpY3RvYyxmYWN0b2V4dHJhLCBOYkNsdXN0LCBCaW9jTWFuYWdlciwgbmFuaWFyLCBEYXRhRXhwbG9yZXIsIA0KICAgICAgIHRpZHl2ZXJzZSwgcHVycnIsIGRwbHlyLCByZWFkeGwsIHJlYWRyLHN0YXRzLCBEZXNjVG9vbHMsIGNsYXNzLGRldnRvb2xzLGltYWdlcixrbml0cixrYWJsZUV4dHJhKSNzb3VyY2UsY29tcGFyZUdyb3VwcyANCiNDYXJnYW5kbyBmdW5jaW9uZXMgZGUgdXN1YXJpbw0Kc291cmNlKCJmdW5jaW9uZXMuUiIpDQpgYGANCg0KYGBge3IgfQ0KbGlicmFyeShyZXRpY3VsYXRlKQ0KdXNlX3B5dGhvbihwYXRoX3B5dGhvbikNCmBgYA0KDQpgYGB7cHl0aG9ufQ0KaW1wb3J0IHBhbmRhcyBhcyBwZA0KZnJvbSBza2xlYXJuLmNsdXN0ZXIgaW1wb3J0IEJpcmNoDQpkZl9lamVtcGxvID0gcGQuRGF0YUZyYW1lKFsyMiw5LDEyLDE1XSxjb2x1bW5zPVsnUHVudG9zJ10pDQoNCmJyYzAgPSBCaXJjaCh0aHJlc2hvbGQ9NSwNCiAgICAgICAgICAgIGJyYW5jaGluZ19mYWN0b3I9Mw0KICAgICAgICAgICAgKQ0KYnJjMC5maXQoZGZfZWplbXBsbykNCg0KcGQuY29uY2F0KFtkZl9lamVtcGxvLHBkLkRhdGFGcmFtZShicmMwLnByZWRpY3QoZGZfZWplbXBsbyksY29sdW1ucz1bJ0NsdXN0ZXInXSldLGF4aXM9MSkNCg0KYGBgDQogDQoNCg0KDQojICAqKkxlY3R1cmEgZGUgZGF0b3MgKioNCg0KYGBge3J9DQojIENhcmdhbW9zIGxhIGRhdGENCmF5YWN1Y2hvIDwtIHJlYWQuY3N2KCJEYXRhLmNzdiIpDQpoZWFkXzU9aGVhZChheWFjdWNobykNCmthYmxlKGhlYWRfNSxjYXB0aW9uID0gIkF5YWN1Y2hvIikgJT4lIGthYmxlX3N0eWxpbmcoInN0cmlwZWQiKSAlPiUgc2Nyb2xsX2JveCh3aWR0aCA9ICIxMDAlIikNCmBgYA0KDQpgYGB7cn0NCnN0cihheWFjdWNobykNCiNza2ltKGF5YWN1Y2hvKQ0KI2Rlc2NyaWJlKGF5YWN1Y2hvKQ0KYGBgDQoNCiMgKipQcmVwcm9jZXNhbWllbnRvIGRlIGRhdG9zKioNCg0KYGBge3J9DQojIEZhY3Rvcml6YWNpw7NuIGRlIHZhcmlhYmxlcyBubyBuw7ptZXJpY2FzDQp2YXJpYWJsZXNfZmFjIDwtIGMoIkRpcmVzYSIsICJNaWNyb3JlZCIsICJFRVNTIiwgIkRwdG9fRUVTUyIsICJQcm92X0VFU1MiLCAiRGlzdF9FRVNTIiwgIlJlbmlwcmVzcyIsICJTZXhvIiwgIkp1bnRvcyIsICJTSVMiLCAiUWFsaXdhcm1hIiwgIkNyZWQiLCAiU3VwbGVtZW50YWNpb24iLCAiQ29uc2VqZXJpYSIsICJTZXNpb24iKQ0KYXlhY3VjaG9bLHZhcmlhYmxlc19mYWNdIDwtIGxhcHBseShheWFjdWNob1ssdmFyaWFibGVzX2ZhY10sIGZhY3RvcikNCiNzdHIoYXlhY3VjaG8pIGNhbWJpbw0KYGBgDQoNCg0KIyMgRGF0b3MgUGVyZGlkb3MNCg0KYGBge3J9DQpheWEgPC1heWFjdWNob1ssMTQ6MjNdDQpwbG90X21pc3NpbmcoYXlhKQ0KYGBgDQoNCl9FbCBhbsOhbGlzaXMgbXVlc3RyYSBxdWUgbGFzIHZhcmlhYmxlcyBzZWxlY2Npb25hZGFzIG5vIHByZXNlbnRhbiB2YWxvcmVzIHBlcmRpZG9zIG8gZmFsdGFudGVzLCBwb3IgbG8gdGFudG8sIG5vIHNlIHJlcXVpZXJlIHJlYWxpemFyIHVuIHByb2NlZGltaWVudG8gZGUgaW1wdXRhY2nDs24uXw0KDQojIyBBbmFsaXNpcyBkZSBPdXRsaWVycw0KDQpgYGB7cn0NCmdnX2JveF9kZW5zaXR5KGF5YSkNCmBgYA0KDQpfQSBwYXJ0aXIgZGUgbG9zIGdyw6FmaWNvcyBhbnRlcmlvcmVzIHNlIG9ic2VydmEgcXVlIHRvZGFzIGxhcyB2YXJpYWJsZXMgYSBleGNlcGNpw7NuIGRlIEVkYWRNZXNlcyBwb3NlZW4gdmFsb3JlcyBvdXRsaWVycyB5IGV4dHJlbW9zIGVuIGFsZ3Vub3MgY2Fzb3MsIGNvbW8gbGFzIHZhcmlhYmxlcyBoZW1vZ2xvYmluYSB5IEFsdHVyYVJlbiB5IEhlbW9nbG9iaW5hYWp1c3RhZGEuXw0KDQoNCiMjIFRyYXRhbWllbnRvIGRlIGRhdG9zIE91dGxpZXJzDQoNCmBgYHtyfQ0KY29scz1jKCdQZXNvJywnVGFsbGEnLCdJTUMnLCdQVFonLCdaVEUnLCdaUEUnLCdIZW1vZ2xvYmluYScsJ0FsdHVyYVJFTicsJ0hlbW9nbG9iaW5hYWp1c3RhZGEnKQ0KYXlhPWZuX291dGxpZXJzKGF5YSxjb2xzLDIsMS41KQ0KYGBgDQoNCmBgYHtyfQ0KZ2dfYm94X2RlbnNpdHlfMihheWEpDQpgYGANCg0KX0VsIHByb2NlZGltaWVudG8gcGFyYSB0cmF0YXIgbG9zIHZhbG9yZXMgb3V0bGllcnMgeSBleHRyZW1vcyBjb25zaXN0acOzIGVuIGFjb3RhciBsb3MgdmFsb3JlcyBzdXBlcmlvcmVzIHkgaW5mZXJpb3JlcyBkZSBsYXMgdmFyaWFibGVzIG1lZGlhbnRlIGVsIHByaW5jaXBpbyBkZSAxLjUgdmVjZXMgZWwgcmFuZ28gaW50ZXJjdWFydMOtbGljbywgc3VtYW5kbyBlc3RlIHZhbG9yIGFsIHBlcmNlbnRpbCA3NSBwYXJhIGRlZmluaXIgZWwgdmFsb3Igc3VwZXJpb3IgZGUgbGEgdmFyaWFibGUgeSByZXN0YW5kbyBlbCB2YWxvciBpbmRpY2FkbyBhbCBwZXJjZW50aWwgMjUgcGFyYSBkZWZpbmlyIGVsIHZhbG9yIGluZmVyaW9yIG3DoXhpbW8gYWNlcHRhZG8uXw0KDQpfTG9zIHZhbG9yZXMgcXVlIGZ1ZXJvbiBzdXBlcmlvcmVzIG8gaW5mZXJpb3JlcyBhIGxvcyB2YWxvcmVzIGVuIGVsIHJhbmdvIGRlZmluaWRvcyBmdWVyb24gcmVlbXBsYXphZG9zIHBvciBlbCBtw6F4aW1vIHkgbcOtbmltbyBjYWxjdWxhZG8gZGUgZGljaG8gcmFuZ28uXw0KDQojIyBDb3JyZWxhY2nDs24NCg0KYGBge3J9DQojY29yIDwtIGF5YWN1Y2hvWywxNDoyM10lPiUgY2hhcnQuQ29ycmVsYXRpb24oaGlzdG9ncmFtPVRSVUUsIHBjaD0xNSkgDQpjb3JyZWxhY2lvbjwtcm91bmQoY29yKGF5YWN1Y2hvWywxNDoyM10pLCAyKQ0KY29ycnBsb3QoY29ycmVsYWNpb24sIG1ldGhvZD0ibnVtYmVyIiwgdHlwZT0idXBwZXIiLG51bWJlci5jZXggPSAwLjcyLHRsLmNleCA9IDAuOCxhZGRDb2VmLmNvbCA9IDAuNSkNCmBgYA0KDQpfRGUgYWN1ZXJkbyBjb24gZWwgYW7DoWxpc2lzIGRlIGNvcnJlbGFjacOzbiBkZSBQZWFyc29uIHNlIGludGVycHJldGEgcXVlIG5vIGV4aXN0ZW4gcmVsYWNpw7NuIGxpbmVhbCBuZWdhdGl2YSBlbnRyZSBsYXMgdmFyaWFibGVzLl8NCg0KX0V4aXN0ZSB1bmEgY29ycmVsYWNpw7NuIGxpbmVhbCBwb3NpdGl2YSBmdWVydGUgZW50cmUgbGFzIHZhcmlhYmxlczpfDQoNCl9FZGFkTWVzZXMgeSBUYWxsYTogMC44Nl8NCl9QZXNvIHkgVGFsbGE6IDAuODRfDQpfUGVzbyBlIElNQzogMC45Ml8NCl9JTUMgeSBQVFo6IDAuODJfDQpfSU1DIHkgWlBFOiAwLjc4Xw0KX1BUWiB5IFpQRTogMC44NF8NCl9IZW1vZ2xvYmluYSB5IEhlbW9nbG9iaW5hYWp1c3RhZGE6IDAuODRfDQoNCl9FeGlzdGUgdW5hIGNvcnJlbGFjacOzbiBsaW5lYWwgcG9zaXRpdmEgbW9kZXJhZGEgZW50cmUgbGFzIHZhcmlhYmxlczpfDQpfRWRhZE1lc2VzIHkgUGVzbzogMC42OV8NCl9FZGFkTWVzZXMgeSBJTUM6IDAuNDRfDQpfUGVzbyB5IFBUWjogMC41NV8NCl9QZXNvIHkgWlRFOiAwLjQyXw0KX1Blc28geSBaUEU6IDAuNjFfDQpfVGFsbGEgeSBaUEU6IDAuNDJfDQpfSGVtb2dsb2JpbmEgeSBBbHR1cmFSZW46IDAuNDRfDQoNCg0KDQo8IS0tIEJhY2t1cC0tPg0KYGBge3IgaW5jbHVkZT1GQUxTRX0gDQpheWFfYmtwPC0gYXlhIA0KYGBgDQoNCg0KIyMgRXN0YW5kYXJpemFjacOzbiBkZSBsb3MgZGF0b3MNCg0KYGBge3J9DQpheWEgPC0gYXMuZGF0YS5mcmFtZShzY2FsZShheWEpKQ0KYGBgDQoNCg0KIyAqKkRldGVybWluYW5kbyBmYWN0aWJpbGlkYWQgZGVsIGNsdXN0ZXIqKg0KDQojIyBNYXRyaXogZGUgZGlzdGFuY2lhIGV1Y2xpZGlhbmENCg0KYGBge3J9DQpkaXMuRGF0YSA8LSBkaXN0KGF5YSwgbWV0cmljICA9IGMoImV1Y2xpZGVhbiIpKSAjIE1hdHJpeiBkZSBkaXN0YW5jaWEgI2NhbWJpbw0KI2Rpcy5EYXRhICAgIDwtIGRhaXN5KGF5YSwgbWV0cmljPSAiZXVjbGlkZWFuIixzdGFuZCA9IFRSVUUpICMgTWF0cml6IGRlIGRpc3RhbmNpYQ0KYGBgDQoNCiMjIFZpc3VhbGl6YW5kbyBsYSBtYXRyaXogZGUgZGlzdGFuY2lhIGNvbiBmdml6X2Rpc3QoKQ0KDQpgYGB7cn0NCiBsaWJyYXJ5KGltYWdlcikNCiBpZiAgKGZpbGUuZXhpc3RzKCJncmFmX21heC5kaXN0LnBuZyIpKSB7ICAgICAgI0NhbWJpbyAgDQogICAgIGltZyA8LSBsb2FkLmltYWdlKCdncmFmX21heC5kaXN0LnBuZycpDQogICAgIHBsb3QoaW1nKQ0KICB9DQojZWxzZXsNCiAgIyBmdml6X2Rpc3QoZGlzLkRhdGEpDQogIyB9DQpgYGANCg0KX1NlIHJlYWxpesOzIGVsIGPDoWxjdWxvIHkgdmlzdWFsaXphY2nDs24gbGEgbWF0cml6IGRlIGRpc3RhbmNpYSBldWNsaWRpYW5hIHV0aWxpemFuZG8gbGFzIGZ1bmNpb25lcyBmdml6X2Rpc3QgKCkgZW4gZWwgcGFxdWV0ZSBmYWN0b2V4dHJhIHIuXw0KX1NlIG9ic2VydmFuIMOhcmVhcyBzb21icmVhZGFzIGRlIGNvbG9yIHJvam8gcXVlIGluZGljYW4gbGEgY2VyY2Fuw61hIGRlIGxhcyBvYnNlcnZhY2lvbmVzIGVuIGJhc2UgYWwgY8OhbGN1bG8gZGUgZGlzdGFuY2lhIGluZGljYWRvLiBFc3RvIHBlcm1pdGUgaW5mZXJpciBxdWUgZWwgc2V0IGRlIGRhdG9zIGVzIGFncnVwYWJsZS5fDQoNCg0KIyMgRXN0YWTDrXN0aWNvIEhvcGtpbnMNCg0KYGBge3J9DQpyZV9wcm9jZXNzPUYNCmlmICAoZmlsZS5leGlzdHMoInJlcy5Ib3BraW5zLlJEYXRhIikgJiAhcmVfcHJvY2VzcykgeyAgICAgICNDYW1iaW8gIA0KICAgICBsb2FkKCdyZXMuSG9wa2lucy5SRGF0YScpIA0KfWVsc2V7DQogIHJlcy5Ib3BraW5zIDwtIGdldF9jbHVzdF90ZW5kZW5jeShheWEsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICBuID0gbnJvdyhheWEpIC0gMSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIGdyYXBoID0gRkFMU0UsIHNlZWQgPSAyMDIyKQ0KICBzYXZlKHJlcy5Ib3BraW5zLGZpbGU9J3Jlcy5Ib3BraW5zLlJEYXRhJykNCn0NCnJlcy5Ib3BraW5zJGhvcGtpbnNfc3RhdA0KYGBgDQoNCl9TZSByZWFsaXrDsyBsYSBwcnVlYmEgZXN0YWTDrXN0aWNhIGRlIEhvcGtpbnMsIHV0aWxpemFuZG8gMCw1IGNvbW8gZWwgdW1icmFsIHBhcmEgZGV0ZXJtaW5hciBxdWUgZXMgcG9jbyBwcm9iYWJsZSBxdWUgZWwgc2V0IGRlIGRhdG9zIHRlbmdhIGNvbmdsb21lcmFkb3MgZXN0YWTDrXN0aWNhbWVudGUgc2lnbmlmaWNhdGl2b3MuIE9ic2VydmFtb3MgcXVlIGVsIHZhbG9yIHJlc3VsdGFudGUgZGUgMC44NTMgc2UgYWNlcmNhIGEgMSwgZW50b25jZXMgcG9kZW1vcyBjb25jbHVpciBxdWUgZWwgY29uanVudG8gZGUgZGF0b3MgZXMgc2lnbmlmaWNhdGl2YW1lbnRlIGFncnVwYWJsZS5fDQoNCiMgKipBbsOhbGlzaXMgY2x1c3RlciBqZXJhcnF1aWNvKiogKGhjbHVzdCB7c3RhdHN9KQ0KDQojIyBBbmFsaXphbmRvIGVsIG1ldG9kbyBkZSBlbmxhY2Ugb3B0aW1vIChjb2VmaWNpZW50ZSBkZSBhZ2xvbWVyYWNpw7NuKQ0KIA0KYGBge3J9DQptZXRvZG9zPSBjKCJzaW5nbGUiLCAiY29tcGxldGUiLCAiYXZlcmFnZSIsICJ3YXJkLkQiLCAid2FyZC5EMiIpDQpDT0VGLkFHTChkaXMuRGF0YSxtZXRvZG9zKQ0KYGBgDQoNCl9IYWNlbW9zIHVzbyBkZSBsYSBjb21wYXJhY2nDs24gZGUgbG9zIGNvZWZpY2llbnRlcyBkZSBhZ2xvbWVyYWNpw7NuIHBhcmEgZGV0ZXJtaW5hciBlbCBuw7ptZXJvIGRlIGNvbmdsb21lcmFkb3MgeSBwcm9wb25lciBlbCBtZWpvciBlc3F1ZW1hIGRlIGFncnVwYWNpw7NuIGEgcGFydGlyIGRlIGxvcyBkaWZlcmVudGVzIHJlc3VsdGFkb3Mgb2J0ZW5pZG9zIGFsIHZhcmlhciB0b2RhcyBsYXMgY29tYmluYWNpb25lcyBkZSBtw6l0b2RvcyBkZSBlbmxhY2U6ICJzaW5nbGUiLCAiY29tcGxldGUiLCAiYXZlcmFnZSIsICJ3YXJkLkQiLCAid2FyZC5EMiIuIENvbiBiYXNlIGVuIGVsIHJlc3VsdGFkbyBlbiBsYSBncsOhZmljYSBzZSBwdWVkZSBpbmRpY2FyIHF1ZSBlbCBlbmxhY2Ugb3B0aW1vIGVzIGVsIGRlOiBXYXJkLkRfDQoNCiMjIEFuYWxpemFuZG8gZWwgbWV0b2RvIGRlIGVubGFjZSBvcHRpbW8gKG1hdHJpeiBjb2ZlbmV0aWNhKQ0KDQpgYGB7cn0NCiNEZWJpZG8gYSBxdWUgZ3JhZmljYW1lbnRlIG5vIGVzdGFuIG11eSBhbGVqYWRvcyBjb24gY29lZmljaWVudGVzIGRlIGFnbG9tZXJhY2lvbiwgcHJvYmFyZW1vcyBlbCBtZXRvZG8gZGUNCiNjb3JyZWxhY2lvbiBjb2ZlbmV0aWNhIHBhcmEgZGVmaW5pciBlbCBtZWpvciBtZXRvZG8gZGUgZW5sYWNlDQpyZV9wcm9jZXNzPUZBTFNFDQppZihmaWxlLmV4aXN0cygiZGZfY29mLlJEYXRhIikgJiAhcmVfcHJvY2VzcykgeyAgICAgICNDYW1iaW8gIA0KICAgICBsb2FkKCdkZl9jb2YuUkRhdGEnKSANCn1lbHNlew0KICBtZXRvZG9zPWMoIndhcmQuRCIsICJ3YXJkLkQyIiwiY29tcGxldGUiLCAiYXZlcmFnZSIsICJzaW5nbGUiKQ0KICBkZl9jb2Y9Zm5fY29uZmVuZXRpY28oZGlzLkRhdGEsbWV0b2RvcykNCiAgc2F2ZShkZl9jb2YsZmlsZT0nZGZfY29mLlJEYXRhJykNCn0NCmthYmxlKGRmX2NvZixjYXB0aW9uID0gIkNvcnJlbGFjaW9uX2NvZmVuZXRpY2EiKSU+JSBrYWJsZV9zdHlsaW5nKCJzdHJpcGVkIikNCmBgYA0KDQpfUmVhbGl6YW1vcyBlbCBhbsOhbGlzaXMgZGUgbGEgbWF0cml6IGNvZmVuZXRpY2EgcGFyYSBkZXRlcm1pbmFyIGxhIHNpbWlsYXJpZGFkIGVudHJlIGxhIG1hdHJpeiBkZSBkaXN0YW5jaWEgb3JpZ2luYWwgKGV1Y2xpZGlhbmEpIHkgbGEgbWF0cml6IGRlIGxhcyB1bmlvbmVzIGRlIGxhcyBvYnNlcnZhY2lvbmVzIGVuIGxvcyBjbHVzdGVyIGplcsOhcnF1aWNvcyBlZmVjdHVhZG9zIGNvbiBsb3MgbcOpdG9kb3MgZGUgZW5sYWNlcyBpbmljaWFsZXMuIENvbW8gcmVzdWx0YWRvIHNlIG9ic2VydmEgcXVlIG5vIGhheSB1bmEgY29ycmVsYWNpw7NuIGZ1ZXJ0ZSBwZXJvIGVsIG3DqXRvZG8gYXZlcmFnZSBwcmVzZW50YSBsYSBtYXlvciBjb3JyZWxhY2nDs24uIFNlIGRlY2lkZSBldmFsdWFyIGVsIGFuw6FsaXNpcyBkZSBjbHVzdGVyIGplcsOhcnF1aWNvIGNvbiBhbWJvcyBtw6l0b2RvczogV2FyZC5EIHkgYXZlcmFnZS5fDQoNCiMjIE9idGVuaWVuZG8gZWwgY2x1c3RlciBjb24gZWwgbWV0b2RvOiAqKldhcmQuRCoqDQoNCmBgYHtyfQ0KQ2x1c19BR19XIDwtIGhjbHVzdChkaXMuRGF0YSxtZXRob2Q9IndhcmQuRCIpDQpgYGANCg0KIyMgRGV0ZXJtaW5hbmRvIGRlIG1hbmVyYSBncmFmaWNhIGVsIG51bWVybyBkZSBjbHVzdGVycw0KDQoNCg0KYGBge3J9DQpsaWJyYXJ5KGdnaGlnaGxpZ2h0KQ0KbG9uZ2l0dWQ9bGVuZ3RoKENsdXNfQUdfVyRoZWlnaHQpDQphbHR1cmFzIDwtIGRhdGEuZnJhbWUoZXRhcGEgPSAxOmxvbmdpdHVkLCBkaXN0YW5jaWEgPSBDbHVzX0FHX1ckaGVpZ2h0KQ0KZ2dwbG90KGFsdHVyYXMpICsgYWVzKHggPSBldGFwYSwgeSA9IGRpc3RhbmNpYSkgICsNCiAgZ2VvbV9wb2ludCgpICsgZ2VvbV9saW5lKCkgICsgDQogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMSwgbG9uZ2l0dWQsIDEwMDApKSArIA0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSA3NTk3LCBjb2wgPSAicmVkIiwgbHR5ID0gMikgKyANCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCAgPSByb3VuZChkaXN0YW5jaWEsMSkpLA0KICAgICAgICAgICAgc2l6ZSA9IDMsIGhqdXN0PSArMSwgdmp1c3Q9IC0xKSArDQogIHRoZW1lX2NsYXNzaWMoKSAjKyBnZ2hpZ2hsaWdodChkaXN0YW5jaWEgPiAxMSkNCmBgYA0KPCEtLSBWaXN1YWxtZW50ZSBkZXRlcm1pbmFtb3MgcXVlIHBvZHJpYW1vcyB0cmFiYWphciBjb24gMDIgbyAwMyBjbHVzdGVycy0tPg0KDQpfRWwgbcOpdG9kbyBncmFmaWNvIG5vcyBzdWdpZXJlIGxhIGV4aXN0ZW5jaWEgZGUgMiBhIDUgYWdydXBhY2lvbmVzLl8NCg0KDQojIyBEZXRlcm1pbmFkbyBlbCBuw7ptZXJvIGRlIGNsdXN0ZXJzIG9wdGltbw0KDQpgYGB7cn0gDQpyZV9wcm9jZXNzPUYNCmlmICAoZmlsZS5leGlzdHMoInJlcy5uYmNsdXN0Vy5SRGF0YSIpJiAhcmVfcHJvY2VzcykgeyAgICAgICNDYW1iaW8gIA0KICAgICBsb2FkKCdyZXMubmJjbHVzdFcuUkRhdGEnKSANCn1lbHNlew0KICBzZWVkID0gMjAyMg0KICByZXMubmJjbHVzdFcgPC0gTmJDbHVzdChheWEsIGRpc3RhbmNlID0iZXVjbGlkZWFuIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgbWluLm5jID0gMiwgbWF4Lm5jID0gOCwgbWV0aG9kID0gIndhcmQuRCIsIGluZGV4ID0iYWxsIikgI0NhbWJpbw0KICBzYXZlKHJlcy5uYmNsdXN0VyxmaWxlPSdyZXMubmJjbHVzdFcuUkRhdGEnKQ0KfQ0KcGFyKG1mcm93PWMoMSwxKSkgIA0KZnZpel9uYmNsdXN0X3gocmVzLm5iY2x1c3RXKQ0KYGBgDQoNCl9NZWRpYW50ZSBlbCB1c28gZGUgcGFxdWV0ZSBOYkNsdXN0IHBvZGVtb3MgZGV0ZXJtaW5hciBlbCBtZWpvciBuw7ptZXJvIGRlIGFncnVwYWNpb25lcyB0b21hbmRvIGVuIGN1ZW50YSBsb3MgZGlmZXJlbnRlcyByZXN1bHRhZG9zIG9idGVuaWRvcyBhbCB2YXJpYXIgdG9kYXMgbGFzIGNvbWJpbmFjaW9uZXMgbGEgY2FudGlkYWQgZGUgY2zDunN0ZXIgZGVzZWFkb3MgcGFyYSBlbCBtw6l0b2RvIGRlIGVubGFjZSBlbGVnaWRvOiBXYXJkLkQuXw0KDQojIyBSZWFsaXphbmRvIGNsdXN0ZXJpbmcgY29uIEs9Mw0KICANCmBgYHtyfQ0KIyBDb3J0YW5kbyBlbiAyIGNsdWVzdGVyDQpncnBfV1I9Y3V0cmVlKENsdXNfQUdfVywgayA9IDMpDQojIE51bWJlciBkZSBjYXNvcyBlbiBjYWRhIGNsdXN0ZXINCnRhYmxlKGdycF9XUikNCiMgRGVzY3JpcGNpw7NuIGRlIGNhZGEgY2x1c3Rlcg0KbWVkPC1hZ2dyZWdhdGUoYXlhX2JrcCwgYnk9bGlzdChjbHVzdGVyPWdycF9XUiksIG1lYW4pICNtZWRpYXMNCmthYmxlKG1lZCkgJT4lIGthYmxlX3N0eWxpbmcoInN0cmlwZWQiKSAlPiUgc2Nyb2xsX2JveCh3aWR0aCA9ICIxMDAlIikNCiNrbml0cjo6a2FibGUobWVkLCBmb3JtYXQgPSAibWFya2Rvd24iKQ0KYGBgDQoNCl9TZSBwcm9jZWRpw7MgYSBjb3J0YXIgbGEgYWdydXBhY2nDs24gZ2VuZXJhbCAoZGVuZG9ncmFtYSkgZW4gMDMgY2zDunN0ZXJzIHRvbWFuZG8gY29tbyBiYXNlIGVsIHJlc3VsdGFkbyDDs3B0aW1vIGRlIE5iY2x1c3QgeSBvYnRlbmVtb3MgbGEgY2FudGlkYWQgZGUgb2JzZXJ2YWNpb25lcyBwb3IgY2FkYSB1bm8gZGUgZWxsb3MuXw0KDQoNCiMjIENhcmFjdGVyw616YW5kbyBsb3MgY2x1c3Rlcg0KDQojIyMgR3JhZmljYW5kbyBsb3MgY2x1c3RlcnMuDQoNCmBgYHtyfQ0KI2Z2aXpfZGVuZChDbHVzX0FHLCBrID0gMiwgY2V4ID0gMC43LCBob3JpeiA9IEZBTFNFLCBrX2NvbG9ycyA9ICJqY28iLA0KICAgICAgICAgICNyZWN0ID0gVFJVRSwgcmVjdF9ib3JkZXIgPSAiamNvIiwgcmVjdF9maWxsID0gVFJVRSkNCmBgYA0KDQojIyMgRGlhZ3JhbWEgZGUgY2FyYWN0ZXJpemFjacOzbiAtIGxpbmVhcw0KDQpgYGB7cn0NCg0KZGF0YV9wbG90PXNjYWxlKGF5YV9ia3ApICNTY2FsZQ0KI2RhdGFfcGxvdD1hcHBseShheWFfYmtwLCAyLCBub3JtYWxpemUpICNNYXgtTWluDQpNPC1hcy5kYXRhLmZyYW1lKHQocmJpbmQoYWdncmVnYXRlKGRhdGFfcGxvdCwgYnk9bGlzdChjbHVzdGVyPWdycF9XUiksIG1lYW4pWywtMV0pKSkNCmE9YXMudmVjdG9yKGNvbE1lYW5zKGRhdGFfcGxvdCkpDQpmaW49ZGF0YS5mcmFtZShNLGEsbmFtZXMoYXlhX2JrcCkpO25hbWVzKGZpbik8LWMoIkNsdXMxIiwiY2x1czIiLCJjbHVzMyIsIk1lZGlhIiwidmFyIikNCiNmaW49ZGF0YS5mcmFtZShNLGEsbmFtZXMoYXlhX2JrcCkpO25hbWVzKGZpbik8LWMoIkNsdXMxIiwiY2x1czIiLCJNZWRpYSIsInZhciIpDQphbGk9bWVsdChmaW4saWQudmFycyA9ICJ2YXIiKQ0KYWduZXMzIDwtIGdncGxvdChhbGksIGFlcyh4PXZhcix5PXJvdW5kKHZhbHVlLDEpLGdyb3VwPXZhcmlhYmxlLGNvbG91cj12YXJpYWJsZSkpICsNCiAgZ2VvbV9wb2ludCgpKyBnZW9tX2xpbmUoYWVzKGx0eT12YXJpYWJsZSkpKyBleHBhbmRfbGltaXRzKHkgPSBjKC0xLjksIDEuOSkpKw0KICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA2MCwgdmp1c3QgPSAwLjUsIGhqdXN0PTEpKSArDQogICBsYWJzKHRpdGxlPSJEaWFncmFtYSBkZSBsaW5lYXMgZGUgY2x1c3RlciBBR05FUyBwb3IgdmFyaWFibGUgY3VhbnRpdGF0aXZhIGNvbiBLPTMiLA0KICAgICAgIHg9IlZhcmlhYmxlIiwgeSA9ICJ2YWxvciIpDQoNCmFnbmVzMw0KYGBgDQoNCg0KX1NlIGNhbGN1bGEgbG9zIHByb21lZGlvcyBkZSBsYXMgb2JzZXJ2YWNpb25lcyBjb24gbGEgYmFzZSBkZSBkYXRvcyBvcmlnaW5hbCBhZ3J1cGFkbyBwb3IgY2FkYSBjbMO6c3Rlci5fDQpfU2Ugb2JzZXJ2YSBxdWUgZWwgY2zDunN0ZXIgMDEgdGllbmUgbWF5b3IgY2FudGlkYWQgZGUgaW5kaXZpZHVvcyB5IGVzdG9zIHNlIGNhcmFjdGVyaXphbiBwb3IgdGVuZXIgbWF5b3IgcGVzbywgdGFsbGEsIGVsIG3DrW5pbW8gcmV0cmFzbyBlbiBzdSBjcmVjaW1pZW50bywgdW4gw61uZGljZSBudXRyaWNpb25hbCBwb3NpdGl2bywgbWF5b3Igw61uZGljZSBkZSBtYXNhIGNvcnBvcmFsIHkgdml2ZW4gYSB1bmEgYWx0dXJhIG1lbm9yLl8NCl9Qb3Igb3RybyBsYWRvLCBzZSBwdWVkZSBvYnNlcnZhciBxdWUgZWwgY2zDunN0ZXIgMDMgcG9zZWUgbG9zIGluZGl2aWR1b3MgZGUgbWF5b3IgZWRhZCwgcGVybywgZWwgcGVzbywgZWwgw61uZGljZSBkZSBtYXNhIGNvcnBvcmFsLCB0YWxsYSBlcyBtZW5vciBhbCBjbMO6c3RlciAwMSB5IHBvc2VlbiBtYXlvciBkZXNudXRyaWNpw7NuLl8NCg0KDQojIyMgRGlhZ3JhbWEgZGUgY2FyYWN0ZXJpemFjacOzbiAtIGJveHBsb3QNCg0KYGBge3J9DQpkZCA8LSBjYmluZChheWFfYmtwLCBjbHVzdGVyID1ncnBfV1IgKQ0KZGQkY2x1c3RlcjwtYXMuZmFjdG9yKGRkJGNsdXN0ZXIpDQpkZi5tIDwtIG1lbHQoZGQsIGlkLnZhciA9ICJjbHVzdGVyIikNCnAgPC0gZ2dwbG90KGRhdGEgPSBkZi5tLCBhZXMoeD12YXJpYWJsZSwgeT12YWx1ZSkpICsgDQogIGdlb21fYm94cGxvdChhZXMoZmlsbD1jbHVzdGVyKSkrIGZhY2V0X3dyYXAoIH4gdmFyaWFibGUsIHNjYWxlcz0iZnJlZSIpIA0KcA0KYGBgDQoNCl9MYXMgZ3LDoWZpY2FzIHBlcm1pdGVuIGFuYWxpemFyIGxvcyBjbMO6c3RlcmVzIGRlIG1hbmVyYSBpbmRpdmlkdWFsIGEgdHJhdsOpcyBkZSBzdXMgdmFyaWFibGVzLiBQb3IgZWplbXBsbywgZWwgY2zDunN0ZXIgY29uIG1heW9yIGFsdHVyYSBkZSByZXNpZGVuY2lhIGVzIGVsIG7Dum1lcm8gMDMuIEVsIGNsw7pzdGVyIGNvbiBtZW5vciB0YWxsYSAwMywgZWwgY2zDunN0ZXIgY29uIG1heW9yIMOtbmRpY2UgZGUgbWFzYSBjb3Jwb3JhbCBlcyBlbCBjbMO6c3RlciAwMS5fDQoNCiMjIE9idGVuaWVuZG8gZWwgY2x1c3RlciBjb24gZWwgbWV0b2RvOiAqKmF2ZXJhZ2UqKg0KDQpgYGB7cn0NCkNsdXNfQUdfQVYgPC0gaGNsdXN0KGRpcy5EYXRhLG1ldGhvZD0iYXZlcmFnZSIpDQpgYGANCg0KIyMgRGV0ZXJtaW5hbmRvIGRlIG1hbmVyYSBncmFmaWNhIGVsIG51bWVybyBkZSBjbHVzdGVycw0KDQpgYGB7cn0NCmxpYnJhcnkoZ2doaWdobGlnaHQpDQpsb25naXR1ZD1sZW5ndGgoQ2x1c19BR19BViRoZWlnaHQpDQphbHR1cmFzIDwtIGRhdGEuZnJhbWUoZXRhcGEgPSAxOmxvbmdpdHVkLCBkaXN0YW5jaWEgPSBDbHVzX0FHX0FWJGhlaWdodCkNCmdncGxvdChhbHR1cmFzKSArIGFlcyh4ID0gZXRhcGEsIHkgPSBkaXN0YW5jaWEpICArDQogIGdlb21fcG9pbnQoKSArIGdlb21fbGluZSgpICArIA0KICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDEsIGxvbmdpdHVkLCAxMDAwKSkgKyANCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gNzU5NywgY29sID0gInJlZCIsIGx0eSA9IDIpICsgDQogIGdlb21fdGV4dChhZXMobGFiZWwgID0gcm91bmQoZGlzdGFuY2lhLDEpKSwNCiAgICAgICAgICAgIHNpemUgPSAzLCBoanVzdD0gKzEsIHZqdXN0PSAtMSkgKw0KICB0aGVtZV9jbGFzc2ljKCkgIysgZ2doaWdobGlnaHQoZGlzdGFuY2lhID4gMTEpDQpgYGANCjwhLS0gVmlzdWFsbWVudGUgZGV0ZXJtaW5hbW9zIHF1ZSBwb2RyaWFtb3MgdHJhYmFqYXIgY29uIDAyIG8gMDMgY2x1c3RlcnMtLT4NCg0KX0VsIG3DqXRvZG8gZ3JhZmljbyBub3Mgc3VnaWVyZSBsYSBleGlzdGVuY2lhIGRlIDIgYSA1IGFncnVwYWNpb25lcy5fDQoNCg0KIyMgRGV0ZXJtaW5hZG8gZWwgbsO6bWVybyBkZSBjbHVzdGVycyBvcHRpbW8NCg0KYGBge3J9IA0KcmVfcHJvY2Vzcz1GQUxTRQ0KaWYgIChmaWxlLmV4aXN0cygicmVzLm5iY2x1c3RBVi5SRGF0YSIpJiAhcmVfcHJvY2VzcykgeyAgICAgICNDYW1iaW8gIA0KICAgICBsb2FkKCdyZXMubmJjbHVzdEFWLlJEYXRhJykgDQp9ZWxzZXsNCiAgc2VlZCA9IDIwMjINCiAgcmVzLm5iY2x1c3RBViA8LSBOYkNsdXN0KGF5YSwgZGlzdGFuY2UgPSJldWNsaWRlYW4iLCANCiAgICAgICAgICAgICAgICAgICAgICAgICBtaW4ubmMgPSAyLCBtYXgubmMgPSA4LCBtZXRob2QgPSAiYXZlcmFnZSIsIGluZGV4ID0iYWxsIikgI0NhbWJpbw0KICBzYXZlKHJlcy5uYmNsdXN0QVYsZmlsZT0ncmVzLm5iY2x1c3RBVi5SRGF0YScpDQp9DQpwYXIobWZyb3c9YygxLDEpKSAgDQpmdml6X25iY2x1c3RfeChyZXMubmJjbHVzdEFWKQ0KYGBgDQoNCl9sb3MgZGlmZXJlbnRlcyByZXN1bHRhZG9zIG9idGVuaWRvcyBhbCB2YXJpYXIgdG9kYXMgbGFzIGNvbWJpbmFjaW9uZXMgbGEgY2FudGlkYWQgZGUgY2zDunN0ZXIgZGVzZWFkb3MgcGFyYSBlbCBtw6l0b2RvIGRlIGVubGFjZSBlbGVnaWRvOiBhdmVyYWdlLl8NCg0KIyMgUmVhbGl6YW5kbyBjbHVzdGVyaW5nIGNvbiBLPTINCiAgDQpgYGB7cn0NCiMgQ29ydGFuZG8gZW4gMiBjbHVlc3Rlcg0KZ3JwX0FWPWN1dHJlZShDbHVzX0FHX0FWLCBrID0gMikNCiMgTnVtYmVyIGRlIGNhc29zIGVuIGNhZGEgY2x1c3Rlcg0KdGFibGUoZ3JwX0FWKQ0KIyBEZXNjcmlwY2nDs24gZGUgY2FkYSBjbHVzdGVyDQptZWQ8LWFnZ3JlZ2F0ZShheWFfYmtwLCBieT1saXN0KGNsdXN0ZXI9Z3JwX0FWKSwgbWVhbikgI21lZGlhcw0Ka2FibGUobWVkKSAlPiUga2FibGVfc3R5bGluZygic3RyaXBlZCIpICU+JSBzY3JvbGxfYm94KHdpZHRoID0gIjEwMCUiKQ0KI2tuaXRyOjprYWJsZShtZWQsIGZvcm1hdCA9ICJtYXJrZG93biIpDQpgYGANCl9TZSByZWFsaXphIGVsIGNvcnRlIGRlIGxhIGFncnVwYWNpw7NuIGdlbmVyYWwgKGRlbmRvZ3JhbWEpIGVuIDAyIGNsw7pzdGVyZXMsIHRvbWFuZG8gY29tbyBiYXNlIGVsIHJlc3VsdGFkbyDDs3B0aW1vIGRlIE5iY2x1c3QgeSBvYnRlbmVtb3MgbGEgY2FudGlkYWQgZGUgb2JzZXJ2YWNpb25lcyBwb3IgY2FkYSB1bm8gZGUgZWxsb3MuIFNlIHB1ZWRlIG9ic2VydmFyIHF1ZSBlbCByZXN1bHRhZG8gcHJlc2VudGEgZG9zIGdydXBvcywgY29uIGxhIG1heW9yIGNhbnRpZGFkIGRlIGluZGl2aWR1b3MgYWdydXBhZG9zIGVuIGNsw7pzdGVyIDAxLl8NCiMjIENhcmFjdGVyw616YW5kbyBsb3MgY2x1c3Rlcg0KDQojIyMgR3JhZmljYW5kbyBsb3MgY2x1c3RlcnMuDQoNCmBgYHtyfQ0KI2Z2aXpfZGVuZChDbHVzX0FHLCBrID0gMiwgY2V4ID0gMC43LCBob3JpeiA9IEZBTFNFLCBrX2NvbG9ycyA9ICJqY28iLA0KICAgICAgICAgICNyZWN0ID0gVFJVRSwgcmVjdF9ib3JkZXIgPSAiamNvIiwgcmVjdF9maWxsID0gVFJVRSkNCmBgYA0KDQojIyMgRGlhZ3JhbWEgZGUgY2FyYWN0ZXJpemFjacOzbiAtIGxpbmVhcw0KDQpgYGB7cn0NCmRhdGFfcGxvdD1zY2FsZShheWFfYmtwKSAjU2NhbGUNCiNkYXRhX3Bsb3Q9YXBwbHkoYXlhX2JrcCwgMiwgbm9ybWFsaXplKSAjTWF4LU1pbg0KTTwtYXMuZGF0YS5mcmFtZSh0KHJiaW5kKGFnZ3JlZ2F0ZShkYXRhX3Bsb3QsIGJ5PWxpc3QoY2x1c3Rlcj1ncnBfQVYpLCBtZWFuKVssLTFdKSkpDQphPWFzLnZlY3Rvcihjb2xNZWFucyhkYXRhX3Bsb3QpKQ0KI2Zpbj1kYXRhLmZyYW1lKE0sYSxuYW1lcyhheWFfYmtwKSk7bmFtZXMoZmluKTwtYygiQ2x1czEiLCJjbHVzMiIsImNsdXMzIiwiTWVkaWEiLCJ2YXIiKQ0KZmluPWRhdGEuZnJhbWUoTSxhLG5hbWVzKGF5YV9ia3ApKTtuYW1lcyhmaW4pPC1jKCJDbHVzMSIsImNsdXMyIiwiTWVkaWEiLCJ2YXIiKQ0KYWxpPW1lbHQoZmluLGlkLnZhcnMgPSAidmFyIikNCmFnbmVzMiA8LSBnZ3Bsb3QoYWxpLCBhZXMoeD12YXIseT1yb3VuZCh2YWx1ZSwxKSxncm91cD12YXJpYWJsZSxjb2xvdXI9dmFyaWFibGUpKSArDQogIGdlb21fcG9pbnQoKSsgZ2VvbV9saW5lKGFlcyhsdHk9dmFyaWFibGUpKSsgZXhwYW5kX2xpbWl0cyh5ID0gYygtMS45LCAxLjkpKSsNCiAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNjAsIHZqdXN0ID0gMC41LCBoanVzdD0xKSkgKw0KICBsYWJzKHRpdGxlPSJEaWFncmFtYSBkZSBsaW5lYXMgZGUgY2x1c3RlciBBR05FUyBwb3IgdmFyaWFibGUgY3VhbnRpdGF0aXZhIGNvbiBLPTIiLA0KICAgICAgIHg9IlZhcmlhYmxlIiwgeSA9ICJ2YWxvciIpDQoNCmFnbmVzMg0KYGBgDQpfRWwgY8OhbGN1bG8gZGVsIHByb21lZGlvIHBvciBjYWRhIGdydXBvIG5vIHBlcm1pdGUgZGlmZXJlbmNpYXJsb3MgY29ycmVjdGFtZW50ZSBjb24gdW5hIG1hcmNhZGEgZGlmZXJlbmNpYSBlbiBtdWNoYXMgdmFyaWFibGVzLl8NCg0KDQoNCg0KIyMjIERpYWdyYW1hIGRlIGNhcmFjdGVyaXphY2nDs24gLSBib3hwbG90DQoNCmBgYHtyfQ0KZGQgPC0gY2JpbmQoYXlhX2JrcCwgY2x1c3RlciA9Z3JwX0FWICkNCmRkJGNsdXN0ZXI8LWFzLmZhY3RvcihkZCRjbHVzdGVyKQ0KZGYubSA8LSBtZWx0KGRkLCBpZC52YXIgPSAiY2x1c3RlciIpDQpwIDwtIGdncGxvdChkYXRhID0gZGYubSwgYWVzKHg9dmFyaWFibGUsIHk9dmFsdWUpKSArIA0KICBnZW9tX2JveHBsb3QoYWVzKGZpbGw9Y2x1c3RlcikpKyBmYWNldF93cmFwKCB+IHZhcmlhYmxlLCBzY2FsZXM9ImZyZWUiKSANCnANCmBgYA0KDQojIyMgVXNhbmRvIGVsIGluZGljZSBkZSByYW5kIHBhcmEgZGVmaW5pciBlbCBjbHVzdGVyIG9wdGltbw0KDQpgYGB7cn0NCmxpYnJhcnkoZm9zc2lsKQ0KcmFuZC5pbmRleChncnBfV1IsZ3JwX0FWKQ0KYGBgDQpTZWd1biBlbCBpbmRpY2UgZGUgUmFuZCwgbm8gc2UgcG9kcmlhIGNvbXByb2JhciBxdWUgbGFzIHNvbHVjaW9uZXMgKHdhcmQuRCB5IGF2ZXJhZ2UpIGNsdXN0ZXJzIHNvbiBwYXJlY2lkb3MsIHBvciBlc2UgbW90aXZvcyBlbGVnaXJlbW9zIGVsIHF1ZSB0aWVuZSBtYXlvciBzZXJwYXJhY8OzbiBhIG5pdmVsIGdyYWZpY28gZGVsIGxpbmVhcy4NCg0KIyAqKkFuw6FsaXNpcyBjbHVzdGVyOiBLIOKAkyBNZWFucyoqDQoNCiMjIERldGVybWluYW5kbyBuw7ptZXJvIMOzcHRpbW8gZGUgY2x1c3RlcnMNCg0KDQoNCiMjIyBTaWxob3VldHRlIG1ldGhvZA0KDQpgYGB7cn0NCnNldC5zZWVkKDEyMykNCnJlX3Byb2Nlc3M9RkFMU0UNCmlmICAoZmlsZS5leGlzdHMoImdnX3NpbC5SRGF0YSIpICYgIXJlX3Byb2Nlc3MpIHsgICAgICAjQ2FtYmlvICANCiAgICAgbG9hZCgnZ2dfc2lsLlJEYXRhJykgDQp9ZWxzZXsNCiAgIGdnX3NpbD1mdml6X25iY2x1c3QoYXlhLCBrbWVhbnMsIG1ldGhvZCA9ICJzaWxob3VldHRlIikrDQogICAgICAgICAgbGFicyhzdWJ0aXRsZSA9ICJTaWxob3VldHRlIG1ldGhvZCIpDQogICBzYXZlKGdnX3NpbCxmaWxlPSdnZ19zaWwuUkRhdGEnKQ0KfQ0KZ2dfc2lsDQpgYGANCg0KX0VsIHJlc3VsdGFkbyBkZWwgw61uZGljZSBkZSBzaWx1ZXRhIGVzdGFibGVjZSBxdWUgcGFyYSBLLW1lYW5zIGVzIMOzcHRpbW8gdXRpbGl6YXIgMyBjbMO6c3RlcmVzLCBkZSBlc3RlIG1vZG8gc2UgZ2VuZXJhcm9uIDMgY2zDunN0ZXJlcyBjb24gMjk2MiwgMjIxMCB5IDI0MjcgcmVzcGVjdGl2YW1lbnRlLl8NCg0KIyMjIEVsYm93IG1ldGhvZCAoV1NTKQ0KYGBge3J9DQpzZXQuc2VlZCgxMjMpDQpyZV9wcm9jZXNzPUZBTFNFDQppZiAgKGZpbGUuZXhpc3RzKCJnZ19XU1MuUkRhdGEiKSAmICFyZV9wcm9jZXNzKSB7ICAgICAgI0NhbWJpbyAgDQogICAgIGxvYWQoJ2dnX1dTUy5SRGF0YScpIA0KfWVsc2V7DQogICBnZ19XU1M9ZnZpel9uYmNsdXN0KGF5YSwga21lYW5zLCBtZXRob2QgPSAid3NzIikgKyBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAzLCBsaW5ldHlwZSA9IDIpKw0KICAgICAgICAgIGxhYnMoc3VidGl0bGUgPSAiRWxib3cgbWV0aG9kIikNCiAgIHNhdmUoZ2dfV1NTLGZpbGU9J2dnX1dTUy5SRGF0YScpDQp9DQpnZ19XU1MNCmBgYA0KDQoNCiMjIFJlYWxpemFuZG8gY2x1c3RlcmluZyBjb24gSz0zDQoNCmBgYHtyfQ0Ka20ucmVzIDwtIGttZWFucyhheWEsIGNlbnRlcnM9Myxuc3RhcnQgPSAyNSkNCmdycF9rbT1rbS5yZXMkY2x1c3Rlcg0KdGFibGUoZ3JwX2ttKQ0KIyBEZXNjcmlwY2nDs24gZGUgY2FkYSBjbHVzdGVyDQptZWQ8LWFnZ3JlZ2F0ZShheWFfYmtwLCBieT1saXN0KGNsdXN0ZXI9Z3JwX2ttKSwgbWVhbikNCmthYmxlKG1lZCkgJT4lIGthYmxlX3N0eWxpbmcoInN0cmlwZWQiKSAlPiUgc2Nyb2xsX2JveCh3aWR0aCA9ICIxMDAlIikNCiNrbml0cjo6a2FibGUobWVkLCBmb3JtYXQgPSAibWFya2Rvd24iKQ0KYGBgDQoNCiMjIENhcmFjdGVyw616YW5kbyBsb3MgY2x1c3Rlcg0KDQojIyMgR3JhZmljYW5kbyBsb3MgY2x1c3RlcnMuDQoNCmBgYHtyfQ0KI2Z2aXpfY2x1c3RlcihrbS5yZXMsIGRhdGEgPSBheWEsDQogICAgICAgICAgICAjIHBhbGV0dGUgPSAiamNvIiwNCiAgICAgICAgICAgICAjZWxsaXBzZS50eXBlID0gImV1Y2xpZCIsICMgQ29uY2VudHJhdGlvbiBlbGxpcHNlDQogICAgICAgICAgICAgI3N0YXIucGxvdCA9IFRSVUUsICMgQWRkIHNlZ21lbnRzIGZyb20gY2VudHJvaWRzIHRvIGl0ZW1zDQogICAgICAgICAgICAgI3JlcGVsID0gVFJVRSwgIyBBdm9pZCBsYWJlbCBvdmVycGxvdHRpbmcgKHNsb3cpDQogICAgICAgICAgICAgI2dndGhlbWUgPSB0aGVtZV9taW5pbWFsKCkpDQpgYGANCg0KIyMjIERpYWdyYW1hIGRlIGNhcmFjdGVyaXphY2nDs24gLSBsaW5lYXMNCg0KYGBge3J9DQpkYXRhX3Bsb3Q9c2NhbGUoYXlhX2JrcCkgI1NjYWxlDQojZGF0YV9wbG90PWFwcGx5KGF5YV9ia3AsIDIsIG5vcm1hbGl6ZSkgI01heC1NaW4NCk08LWFzLmRhdGEuZnJhbWUodChyYmluZChhZ2dyZWdhdGUoZGF0YV9wbG90LCBieT1saXN0KGNsdXN0ZXI9Z3JwX2ttKSwgbWVhbilbLC0xXSkpKQ0KYT1hcy52ZWN0b3IoY29sTWVhbnMoZGF0YV9wbG90KSkNCmZpbj1kYXRhLmZyYW1lKE0sYSxuYW1lcyhheWFfYmtwKSk7bmFtZXMoZmluKTwtYygiQ2x1czEiLCJjbHVzMiIsImNsdXMzIiwiTWVkaWEiLCJ2YXIiKQ0KI2Zpbj1kYXRhLmZyYW1lKE0sYSxuYW1lcyhheWFfYmtwKSk7bmFtZXMoZmluKTwtYygiQ2x1czEiLCJjbHVzMiIsIk1lZGlhIiwidmFyIikNCmFsaT1tZWx0KGZpbixpZC52YXJzID0gInZhciIpDQprbTMgPC0gZ2dwbG90KGFsaSwgYWVzKHg9dmFyLHk9cm91bmQodmFsdWUsMSksZ3JvdXA9dmFyaWFibGUsY29sb3VyPXZhcmlhYmxlKSkgKw0KICBnZW9tX3BvaW50KCkrIGdlb21fbGluZShhZXMobHR5PXZhcmlhYmxlKSkrIGV4cGFuZF9saW1pdHMoeSA9IGMoLTEuOSwgMS45KSkrDQogICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDYwLCB2anVzdCA9IDAuNSwgaGp1c3Q9MSkpICsNCiAgbGFicyh0aXRsZT0iRGlhZ3JhbWEgZGUgbGluZWFzIGRlIGNsdXN0ZXIgSy1tZWFucyBwb3IgdmFyaWFibGUgY3VhbnRpdGF0aXZhIGNvbiBLPTMiLA0KICAgICAgIHg9IlZhcmlhYmxlIiwgeSA9ICJ2YWxvciIpDQoNCmttMw0KYGBgDQpfQ29uIHJlc3BlY3RvIGEgbGEgY2FyYWN0ZXJpemFjacOzbiBkZSBsb3MgY2zDunN0ZXJlcywgc2UgZGVzcHJlbmRlIGRlbCBncsOhZmljbyBxdWUgZWwgdmFsb3IgbcOhcyBjZXJjYW5vIGVudHJlIGNsw7pzdGVyZXMgZXMgbGEgYWx0dXJhLCBtaWVudHJhcyBxdWUgZWwgdmFsb3IgbcOhcyBhbGVqYWRvIGVzIGVsIHBlc28uXw0KDQoNCiMjIyBEaWFncmFtYSBkZSBjYXJhY3Rlcml6YWNpw7NuIC0gYm94cGxvdA0KDQpgYGB7cn0NCmRkIDwtIGNiaW5kKGF5YV9ia3AsIGNsdXN0ZXIgPWdycF9rbSApDQpkZCRjbHVzdGVyPC1hcy5mYWN0b3IoZGQkY2x1c3RlcikNCmRmLm0gPC0gbWVsdChkZCwgaWQudmFyID0gImNsdXN0ZXIiKQ0KcCA8LSBnZ3Bsb3QoZGF0YSA9IGRmLm0sIGFlcyh4PXZhcmlhYmxlLCB5PXZhbHVlKSkgKyANCiAgZ2VvbV9ib3hwbG90KGFlcyhmaWxsPWNsdXN0ZXIpKSsgZmFjZXRfd3JhcCggfiB2YXJpYWJsZSwgc2NhbGVzPSJmcmVlIikgDQpwDQpgYGANCl9FbCBncsOhZmljbyBkZSBjYWphcyBpbmRpY2EgcXVlIGxhIHZhcmlhYmxlIG3DoXMgY2VyY2FuYSBlbnRyZSBjbMO6c3RlcnMgZXMgbGEgYWx0dXJhUkVOLCB5IGxhIG3DoXMgbGVqYW5hIGVzIGVsIElNQy5fDQoNCg0KX1BhcmEgbGEgY2x1c3Rlcml6YWNpw7NuIHV0aWxpemFuZG8gZWwgYWxnb3JpdG1vIEJpcmNoLCBzZSBoaXpvIHVzbyBkZSB1biBUaHJlc2hvbGQgaWd1YWwgYSAwLjQyIHkgdW4gQnJhbmNoaW5nIEZhY3RvciBpZ3VhbCBhIDUwLiBfDQoNCg0KIyAqKkFuw6FsaXNpcyBjbHVzdGVyIEJpcmNoIChyZXRpY3VsYXRlKSoqDQoNCiMjIENvbmZpZ3VyYW5kbyBlbnRvcm5vIGRlIGRlc2Fycm9sbG8NCg0KYGBge3IgfQ0KbGlicmFyeShyZXRpY3VsYXRlKQ0KdXNlX3B5dGhvbihwYXRoX3B5dGhvbikNCmF5YV9wID0gcl90b19weShheWEpICNyLmF5YQ0KYGBgDQoNCiMjIENhcmdhbmRvIGxpYnJlcmlhcyBkZSBweXRob24gZW4gUg0KDQpgYGB7cHl0aG9ufQ0KaW1wb3J0IHBhbmRhcyBhcyBwZCANCmZyb20gc2tsZWFybi5wcmVwcm9jZXNzaW5nIGltcG9ydCBTdGFuZGFyZFNjYWxlciANCmZyb20gc2tsZWFybi5jbHVzdGVyIGltcG9ydCBCaXJjaCANCmBgYA0KDQojIyBEZWZpbmllbmRvIEJpcmNoIGNvbjogdGhyZXNob2xkPTEsIGJyYW5jaGluZ19mYWN0b3I9Mg0KYGBge3B5dGhvbn0NCmRmID0gci5heWFfcCMgT2J0ZW5pZW5kbyBvYmpldG8gUiB0byBweXRob24NCmJyYyA9IEJpcmNoKHRocmVzaG9sZD0wLjQyLGJyYW5jaGluZ19mYWN0b3I9NTAsbl9jbHVzdGVycz0zKQ0KYnJjLmZpdChkZikNCmBgYA0KDQojIyBSZWFsaXphbmRvIHByZWRpY2Npw7NuDQoNCmBgYHtweXRob259DQpkYXRhX3RvdGFsID0gcGQuY29uY2F0KFtkZixwZC5EYXRhRnJhbWUoYnJjLnByZWRpY3QoZGYpKV0sYXhpcz0xKQ0KZGF0YV90b3RhbC5oZWFkKCkNCmBgYA0KDQojIyBQcm9wb3JjaW9uZXMgcG9yIGNhZGEgY2x1c3Rlcg0KDQpgYGB7cHl0aG9ufQ0KZGF0YV90b3RhbC5ncm91cGJ5KDApWydQZXNvJ10uY291bnQoKQ0KYGBgDQoNCl9MYSBwcm9wb3JjacOzbiBwYXJhIGxvcyAzIGNsw7pzdGVyZXMgZXMgZGUgMjc4OCwgMjQwNCB5IDI0MDcgcmVzcGVjdGl2YW1lbnRlLl8NCg0KDQojIyBHdWFyZGFtb3MgZWwgb2JqZXRvIGNvbiBDbHVzdGVyIEJpcmNoDQpQYXJhIGx1ZWdvIHN1YmlybG8gYWwgUg0KYGBge3B5dGhvbn0NCmRhdGFfdG90YWwudG9fZXhjZWwoJ2JpcmNoLnhsc3gnKQ0KYGBgDQoNCiMjIExlZW1vcyBlbCBvYmpldG8gY29uIENsdXN0ZXIgQmlyY2ggZW4gUg0KYGBge3J9DQpiaSA8LSByZWFkX2V4Y2VsKCJiaXJjaC54bHN4IikNCmJpcmNoIDwtIGFzLmRhdGEuZnJhbWUoYmlbLDI6MTJdKQ0KbmFtZXMgKGJpcmNoKVsxMV0gPSAiY2x1c3RlciINCmJpcmNoJGNsdXN0ZXIgPC0gZmFjdG9yKGJpcmNoJGNsdXN0ZXIsIGxldmVscyA9IGMoMCwxLDIpLCBsYWJlbHMgPSBjKDEsMiwzKSkNCg0KaGVhZChiaXJjaCkNCmBgYA0KDQoNCiMjIENhcmFjdGVyw616YW5kbyBsb3MgY2x1c3Rlcg0KYGBge3J9DQpkYXRhX3Bsb3Q9YmlyY2hbMToxMF0NCmNsdXN0ZXI9YmlyY2gkY2x1c3Rlcg0KdGFibGUoY2x1c3RlcikNCiMgRGVzY3JpcGNpw7NuIGRlIGNhZGEgY2x1c3Rlcg0KbWVkPC1hZ2dyZWdhdGUoYXlhLCBieT1saXN0KGNsdXN0ZXI9Y2x1c3RlciksIG1lYW4pDQoNCm1lZA0KDQojIGtuaXRyOjprYWJsZShtZWQpICU+JSBrYWJsZV9zdHlsaW5nKCJzdHJpcGVkIikgJT4lIHNjcm9sbF9ib3god2lkdGggPSAiMTAwJSIpDQoNCmBgYA0KDQojIyBEaWFncmFtYSBkZSBjYXJhY3Rlcml6YWNpw7NuIC0gbGluZWFzDQoNCmBgYHtyfQ0KDQpNPC1hcy5kYXRhLmZyYW1lKHQocmJpbmQoYWdncmVnYXRlKGRhdGFfcGxvdCwgYnk9bGlzdChjbHVzdGVyPWNsdXN0ZXIpLCBtZWFuKVssLTFdKSkpDQoNCmE9YXMudmVjdG9yKGNvbE1lYW5zKGRhdGFfcGxvdCkpDQpmaW49ZGF0YS5mcmFtZShNLGEsbmFtZXMoYXlhX2JrcCkpO25hbWVzKGZpbik8LWMoIkNsdXMxIiwiQ2x1czIiLCJDbHVzMyIsIk1lZGlhIiwidmFyIikNCg0KYWxpPW1lbHQoZmluLGlkLnZhcnMgPSAidmFyIikNCmJpcmNoMyA8LSBnZ3Bsb3QoYWxpLCBhZXMoeD12YXIseT1yb3VuZCh2YWx1ZSwxKSxncm91cD12YXJpYWJsZSxjb2xvdXI9dmFyaWFibGUpKSArDQogIGdlb21fcG9pbnQoKSsgZ2VvbV9saW5lKGFlcyhsdHk9dmFyaWFibGUpKSsgZXhwYW5kX2xpbWl0cyh5ID0gYygtMS45LCAxLjkpKSsNCiAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNjAsIHZqdXN0ID0gMC41LCBoanVzdD0xKSkgKw0KICBsYWJzKHRpdGxlPSJEaWFncmFtYSBkZSBsaW5lYXMgZGUgY2x1c3RlciBCSVJDSCBwb3IgdmFyaWFibGUgY3VhbnRpdGF0aXZhIGNvbiBLPTMiLA0KICAgICAgIHg9IlZhcmlhYmxlIiwgeSA9ICJ2YWxvciIpDQoNCmJpcmNoMw0KYGBgDQoNCg0KX05vIGV4aXN0ZW4gZGlmZXJlbmNpYXMgdGFuIG1hcmNhZGFzIGVuIEFsdHVyYVJFTiBlbnRyZSBlbCBjbHVzdGVyIDEgeSAyLiBFbiBQVFosIFpQRSB5IFpURSB0YW1wb2NvIGhheSBkaWZlcmVuY2lhcyBtYXJjYWRhcyBlbnRyZSBlbCBjbHVzdGVyIDEgeSAyLiBNaWVudHJhcyBxdWUgZGlmZXJlbmNpYXMgbcOhcyBtYXJjYWRhcyBlbnRyZSBIZW1vZ2xvYmluYSwgSGVtb2dsb2JpbmFBanVzdGFkYSwgSU1DLCBQZXNvIHkgVGFsbGFfDQoNCg0KDQojIyMgRGlhZ3JhbWEgZGUgY2FyYWN0ZXJpemFjacOzbiAtIGJveHBsb3QNCg0KDQpgYGB7cn0NCmRkIDwtIGNiaW5kKGF5YV9ia3AsIGNsdXN0ZXIgPWNsdXN0ZXIgKQ0KZGQkY2x1c3RlcjwtYXMuZmFjdG9yKGRkJGNsdXN0ZXIpDQpkZi5tIDwtIG1lbHQoZGQsIGlkLnZhciA9ICJjbHVzdGVyIikNCnAgPC0gZ2dwbG90KGRhdGEgPSBkZi5tLCBhZXMoeD12YXJpYWJsZSwgeT12YWx1ZSkpICsgDQogIGdlb21fYm94cGxvdChhZXMoZmlsbD1jbHVzdGVyKSkrIGZhY2V0X3dyYXAoIH4gdmFyaWFibGUsIHNjYWxlcz0iZnJlZSIpIA0KcA0KDQpgYGANCg0KDQpfRWwgZGlhZ3JhbWEgZGUgY2FqYXMgaW5kaWNhIHF1ZSBsYSB2YXJpYWJsZSBjb24gbG9zIHZhbG9yZXMgbcOhcyBjZXJjYW5vcyBlbnRyZSBjbMO6c3RlcnMgZXMgbGEgQWx0dXJhUkVOLCBtaWVudHJhcyBxdWUgZWwgbcOhcyBhbGVqYWRvIGVzIGxhIEVkYWQgZW4gTWVzZXMuXw0KDQoNCiMgKipWYWxpZGFuZG8gbG9zIGNsdXN0ZXJzKioNCg0KIyMgSW50ZXJuYSAoY29oZXNpw7NuIHkgc2VwYXJhY2nDs24pDQoNCg0KYGBge3J9DQpyZV9wcm9jZXNzPVRSVUUNCiMgaWYgIChmaWxlLmV4aXN0cygiZGZfaW5kZXhzLlJEYXRhIikgJiAhcmVfcHJvY2VzcykgeyAgICAgICNDYW1iaW8gIA0KICAgICAjIGxvYWQoJ2RmX2luZGV4cy5SRGF0YScpIA0KIyB9ZWxzZXsNCiAgICBsaWJyYXJ5KGNsdXN0ZXJTaW0pDQogICAgbGlicmFyeShjbFZhbGlkKQ0KICAgICNJbmRpY2UgZGUgRGF2aWVzLUJvdWxkaW46IGJ1c2NhbW9zIGVsIHZhbG9yIG1hcyBhbHRvIHBvc2libGUNCiAgICBEQmttXyA8LSBpbmRleC5EQihheWEsIGttLnJlcyRjbHVzdGVyLCBjZW50cm90eXBlcyA9ICJjZW50cm9pZHMiKSREQiAja21lYW5zDQogICAgREJIY18gPC1pbmRleC5EQihheWEsIGdycF9XUiwgZD1kaXMuRGF0YSxjZW50cm90eXBlcz0iY2VudHJvaWRzIikkREIgI2hjbHVzdA0KICAgIERCaXJjaF8gPC1pbmRleC5EQihheWEsIGFzLm51bWVyaWMoY2x1c3RlciksIGNlbnRyb3R5cGVzPSJjZW50cm9pZHMiKSREQiAjQmlyY2gNCiAgICANCiAgICAjSW5kaWNlIGRlIGR1bm46IGJ1c2NhbW9zIGVsIHZhbG9yIG1hcyBiYWpvIHBvc2libGUNCiAgICBEbmttXyA8LSBkdW5uKERhdGEgPSBheWEsIGNsdXN0ZXJzID0ga20ucmVzJGNsdXN0ZXIsIGRpc3RhbmNlID0gTlVMTCkja21lYW5zDQogICAgRG5IY18gPC0gZHVubihkaXMuRGF0YSwgZ3JwX1dSKSAjaGNsdXN0DQogICAgRG5CaXJjaF8gPC0gZHVubihEYXRhID0gYXlhLCBjbHVzdGVycyA9IGFzLm51bWVyaWMoY2x1c3RlciksIGRpc3RhbmNlID0gTlVMTCkjQmlyY2gNCiAgICANCiAgICB0aXBvXz1jKCdrbWVhbnMnLCdoY2x1c3QnLCdCaXJjaCcpDQogICAgZGF2aWVzLmJvdWxkaW5fPWMoREJrbV8sREJIY18sREJpcmNoXykNCiAgICBkdW5uXz1jKERua21fLERuSGNfLERuQmlyY2hfKQ0KICAgIGRmX2luZGV4cz1kYXRhLmZyYW1lKHRpcG9fLGRhdmllcy5ib3VsZGluXyxkdW5uXykNCiAgICBjb2xuYW1lcyhkZl9pbmRleHMpPWMoJ0NsdXN0ZXInLCdEYXZpZXMuQm91bGRpbicsJ0R1bm4nKQ0KICAgIHNhdmUoZGZfaW5kZXhzLGZpbGU9J2RmX2luZGV4cy5SRGF0YScpDQojIH0NCiAgICAjIGRmX2luZGV4cw0KIyBrYWJsZShkZl9pbmRleHMpICU+JSBrYWJsZV9zdHlsaW5nKCJzdHJpcGVkIikgJT4lIHNjcm9sbF9ib3god2lkdGggPSAiMTAwJSIpDQpgYGANCg0KDQpgYGB7cn0NCnNpbGhvdWV0dGU8LXJiaW5kKA0KbWVhbihzaWxob3VldHRlKGFzLm51bWVyaWMoa20ucmVzJGNsdXN0ZXIpICxkaXMuRGF0YSlbLDNdKSwgI2ttZWFucw0KbWVhbihzaWxob3VldHRlKGFzLm51bWVyaWMoZ3JwX1dSKSAsZGlzLkRhdGEpWywzXSksICNoY2x1c3QNCm1lYW4oc2lsaG91ZXR0ZShhcy5udW1lcmljKGJpcmNoJGNsdXN0ZXIpICxkaXMuRGF0YSlbLDNdKSAjQmlyY2gNCikNCmBgYA0KDQpgYGB7cn0NCmNiaW5kKGRmX2luZGV4cywgc2lsaG91ZXR0ZSkNCmBgYA0KDQpEZSBhY3VlcmRvIGVsIMOtbmRpY2UgRGF2aWVzLkJvdWxkaW4gdGllbmUgZWwgbWVub3IgdmFsb3IgY29uIGVsIGFsZ29yaXRtbyBrbWVhbnMsIGVsIMOtbmRpY2UgRHVubiB0aWVuZSBlbCBtYXlvciB2YWxvciBjb24gZWwgYWxnb3JpdG1vIEFnbmVzLCB5IGVsIMOtbmRpY2UgZGUgU2lsaG91ZXR0ZSB0aWVuZSBlbCBtYXlvciB2YWxvciBjb24gZWwgYWxnb3JpdG1vIGstbWVhbnM7IHBvciB0YW50bywgYWwgdGVuZXIgZWwgbWVqb3IgdmFsb3IgZW4gZG9zIMOtbmRpY2VzLCBzZSBkZWNpZGUgdHJhYmFqYXIgY29uIGxvcyByZXN1bHRhZG9zIGRlbCBhbGdvcml0bW8gSy1tZWFucy4NCg0KIyAqKlJlc3VsdGFkb3MqKg0KDQojIyBDbHVzdGVycyBwb3IgYWxnb3JpdG1vcyB5IHN1IHByb3BvcmNpw7NuDQoNClNlIG9idHV2aWVyb24gbGFzIHNpZ3VpZW50ZXMgcHJvcG9yY2nDs24gZGUgY2Fzb3MgcG9yIGNsdXN0ZXIgc2Vnw7puIGxhIG1ldG9kb2xvZ8OtYSB1c2FkYToNCg0KICB8ICBHUlVQTyAgfCBBVFJJQlVUTyB8IEFHTkVTIHwgSy1NZWFucyB8IEJJUkNIIHwNCiAgfDotLS0tLS0tOnw6LS0tLS0tLS06fDotLS0tLTp8Oi0tLS0tLS06fDotLS0tLTp8DQogIHwgR3J1cG8gMSB8IENhbnRpZGFkIHwgMywzNjEgfCAgMiw5NjIgIHwgMiw3ODggfA0KICB8ICAgICAgICAgfCAgICAgJSAgICB8IDQ0LjIlIHwgIDM5LjAlICB8IDM2LjclIHwNCiAgfCBHcnVwbyAyIHwgQ2FudGlkYWQgfCAyLDkxOSB8ICAyLDIxMCAgfCAyLDQwNCB8DQogIHwgICAgICAgICB8ICAgICAlICAgIHwgMzguNCUgfCAgMjkuMSUgIHwgMzEuNiUgfA0KICB8IEdydXBvIDMgfCBDYW50aWRhZCB8IDEsMzE5IHwgIDIsNDI3ICB8IDIsNDA3IHwNCiAgfCAgICAgICAgIHwgICAgICUgICAgfCAxNy40JSB8ICAzMS45JSAgfCAzMS43JSB8DQoNCiMjIEFuw6FsaXNpcyBkZSBwZXJmaWxlcw0KDQpQcm9wb3JjacOzbiBkZSBtZW5vcmVzIGNvbiBhbmVtaWE6DQoNCmBgYHtyfQ0KYXlhY3VjaG9fZmluYWw8LWF5YWN1Y2hvDQpheWFjdWNob19maW5hbCRBbmVtaWE8LWlmX2Vsc2UoYXlhY3VjaG8kSGVtb2dsb2JpbmFhanVzdGFkYSoxMD49MTEwLCAnU2luIGFuZW1pYScsIA0KICAgICAgICBpZl9lbHNlKGF5YWN1Y2hvJEhlbW9nbG9iaW5hYWp1c3RhZGEqMTA+PTEwMCwnTGV2ZScsDQogICAgICAgICAgICAgICAgaWZfZWxzZShheWFjdWNobyRIZW1vZ2xvYmluYWFqdXN0YWRhKjEwPj03MCwnTW9kZXJhZGEnLA0KICAgICAgICAgICAgICAgICAgICAgICAgJ0dyYXZlJykpKQ0KICAgICAgICANCmF5YWN1Y2hvX2ZpbmFsPWNiaW5kKGF5YWN1Y2hvX2ZpbmFsLCBjbHVzdGVyPWttLnJlcyRjbHVzdGVyKQ0KDQp0YWJsZShheWFjdWNob19maW5hbCRBbmVtaWEpDQogICAgIA0KDQpgYGANCg0KDQojIyBBbsOhbGlzaXMgZGUgcGVyZmlsZXMgY29uIGNsdXN0ZXIgQWduZXMNCg0KYGBge3J9DQphZ25lczMNCmBgYA0KDQpFbiBjdWFudG8gYSBsYXMgdmFyaWFibGVzIG51bcOpcmljYXMgbGEgZ3LDoWZpY2EgbXVlc3RyYSBlbCBzaWd1aWVudGUgcGF0csOzbjoNCi0gQ2x1c3RlciAxOiBjb25mb3JtYWRvIHBvciBsb3MgbcOheGltb3MgdmFsb3JlcyBkZSBsYSB2YXJpYWJsZSBJTUMsIHBlc28sIFBUWiwgVGFsbGEsIFpQRSwgWlRFOyB5IGNvbiB2YWxvcmVzIGNlcmNhbm9zIGFsIHByb21lZGlvIGdlbmVyYWwgcGFyYSBsYXMgdmFyaWFibGVzIGFsdHVyYSBSRU4sIGVkYWQgZW4gbWVzZXMgeSBoZW1vZ2xvYmluYS4gU2ludGV0aXphbmRvOiBzb24gbG9zIG1lbm9yZXMgZGUgZWRhZCBtZWRpYSwgZGUgbWF5b3IgcGVzbyB5IHRhbGxhLCBkZSB6b25hcyBkZSBhbHR1cmEgcG9yIHBvY28gZGViYWpvIGRlbCBwcm9tZWRpbyB5IGNvbiBoZW1vZ2xvYmluYSBjZXJjYW5hIGFsIHByb21lZGlvIGdlbmVyYWwuDQotIENsdXN0ZXIgMjogY29uZm9ybWFkbyBwb3IgbcOtbmltb3MgdmFsb3JlcyBkZSBsYSB2YXJpYWJsZSBlZGFkLCBoZW1vZ2xvYmluYSwgaGVtb2dsb2JpbmEgYWp1c3RhZGEsIElNQywgUGVzbywgVGFsbGEsIHkgY29uIHZhbG9yZXMgY2VyY2Fub3MgYWwgcHJvbWVkaW8gZ2VuZXJhbCBwYXJhIGxhcyB2YXJpYWJsZXMgUFRaLFpQRSB5IFpURS4gU2ludGV0aXphbmRvOiBzb24gbG9zIG1lbm9yZXMgZGUgbWVub3IgZWRhZCwgbWVub3IgcGVzbywgdGFsbGEgeSBoZW1vZ2xvYmluYSwgZGUgem9uYXMgZGUgYWx0dXJhIHByb21lZGlvLg0KLSBDbHVzdGVyIDM6IGNvbmZvcm1hZG8gcG9yIGxvcyBtw6F4aW1vcyB2YWxvcmVzIGRlIGxhIHZhcmlhYmxlIEFsdHVyYVJFTiwgZWRhZCwgaGVtb2dsb2JpbmEsIGhlbW9nbG9iaW5hIGFqdXN0YWRhOyBjb24gdmFsb3JlcyBjZXJjYW5vcyBhbCBwcm9tZWRpbyBnZW5lcmFsIHBhcmEgbGFzIHZhcmlhYmxlcyBJTUMgeSBwZXNvLCBwZXJvIGNvbiB2YWxvcmVzIG3DrW5pbW9zIHBhcmEgbGFzIHZhcmlhYmxlcyBQVFosIFpQRSwgWlRFLCB5IGNvbiB0YWxsYSBjZXJjYW5hIGFsIHZhbG9yIG3DoXhpbW8uIFNpbnRldGl6YW5kbzogc29uIGxvcyBtZW5vcmVzIGRlIG1heW9yIGVkYWQgKGFscmVkZWRvciBkZSAzIGHDsW9zKSwgbWF5b3IgdGFsbGEgeSBoZW1vZ2xvYmluYSwgYXVucXVlIGNvbiBwZXNvIHByb21lZGlvIHkgZGUgem9uYXMgZGUgbWF5b3IgYWx0dXJhLg0KDQoNCg0KIyMgQW7DoWxpc2lzIGRlIHBlcmZpbGVzIGNvbiBjbHVzdGVyIEstbWVhbnMNCg0KYGBge3J9DQprbTMNCmBgYA0KDQpFbiBjdWFudG8gYSBsYXMgdmFyaWFibGVzIG51bcOpcmljYXMgbGEgZ3LDoWZpY2EgbXVlc3RyYSBlbCBzaWd1aWVudGUgcGF0csOzbjoNCi0gQ2x1c3RlciAxOiBjb25mb3JtYWRvIHBvciBtw61uaW1vcyB2YWxvcmVzIGRlIGxhIHZhcmlhYmxlIGVkYWQsIGhlbW9nbG9iaW5hLCBoZW1vZ2xvYmluYSBhanVzdGFkYSwgSU1DLCBQZXNvLCBUYWxsYSwgeSBjb24gdmFsb3JlcyBjZXJjYW5vcyBhbCBwcm9tZWRpbyBnZW5lcmFsIHBhcmEgbGFzIHZhcmlhYmxlcyBQVFosIFpQRSB5IFpURS4gU2ludGV0aXphbmRvOiBzb24gbG9zIG1lbm9yZXMgZGUgbWVub3IgZWRhZCAoYWxyZWRlZG9yIGRlIHVuIGHDsW8pLCBtZW5vciBwZXNvLCB0YWxsYSB5IGhlbW9nbG9iaW5hLCBkZSB6b25hcyBkZSBhbHR1cmEgcHJvbWVkaW8uDQotIENsdXN0ZXIgMjogY29uZm9ybWFkbyBwb3IgbG9zIG3DoXhpbW9zIHZhbG9yZXMgZGUgbGEgdmFyaWFibGUgSU1DLCBwZXNvLCBQVFosIFRhbGxhLCBaUEUsIFpURTsgeSBjb24gdmFsb3JlcyBjZXJjYW5vcyBhbCBwcm9tZWRpbyBnZW5lcmFsIHBhcmEgbGFzIHZhcmlhYmxlcyBhbHR1cmEgUkVOLCBlZGFkIGVuIG1lc2VzLCBoZW1vZ2xvYmluYSB5IGhlbW9nbG9iaW5hIGFqdXN0YWRhLiBTaW50ZXRpemFuZG86IHNvbiBsb3MgbWVub3JlcyBkZSBlZGFkIGNlcmNhbmEgYWwgcHJvbWVkaW8sIGRlIG1heW9yIHBlc28geSB0YWxsYSwgZGUgem9uYXMgZGUgbWVub3IgYWx0dXJhLCBhdW5xdWUgbXV5IGNlcmNhbmEgYWwgcHJvbWVkaW8sIHkgY29uIGhlbW9nbG9iaW5hIHByb21lZGlvLg0KLSBDbHVzdGVyIDM6IGNvbmZvcm1hZG8gcG9yIGxvcyBtw6F4aW1vcyB2YWxvcmVzIGRlIGxhIHZhcmlhYmxlIEFsdHVyYVJFTiwgZWRhZCwgaGVtb2dsb2JpbmEsIGhlbW9nbG9iaW5hIGFqdXN0YWRhIHkgdGFsbGE7IGNvbiB2YWxvcmVzIGNlcmNhbm9zIGFsIHByb21lZGlvIGdlbmVyYWwgcGFyYSBsYXMgdmFyaWFibGVzIElNQyB5IHBlc28sIHBlcm8gY29uIHZhbG9yZXMgbcOtbmltb3MgcGFyYSBsYXMgdmFyaWFibGVzIFBUWiwgWlBFIHkgWlRFLiBTaW50ZXRpemFuZG86IHNvbiBsb3MgbWVub3JlcyBkZSBtYXlvciBlZGFkLCBtYXlvciB0YWxsYSwgYXVucXVlIGNvbiBwZXNvIHByb21lZGlvIHkgZGUgem9uYXMgZGUgbWF5b3IgYWx0dXJhIHkgbWF5b3IgaGVtb2dsb2JpbmEuDQoNCiMjIEFuw6FsaXNpcyBkZSBwZXJmaWxlcyBjb24gY2x1c3RlciBCSVJDSA0KDQpgYGB7cn0NCmJpcmNoMw0KYGBgDQoNCkVuIGN1YW50byBhIGxhcyB2YXJpYWJsZXMgbnVtw6lyaWNhcyBsYSBncsOhZmljYSBtdWVzdHJhIGVsIHNpZ3VpZW50ZSBwYXRyw7NuOg0KLSBDbHVzdGVyIDE6IGNvbmZvcm1hZG8gcG9yIG1heGltb3MgdmFsb3JlcyBkZSBsYSB2YXJpYWJsZSBlZGFkLCBoZW1vZ2xvYmluYSwgaGVtb2dsb2JpbmEgYWp1c3RhZGEsIElNQywgUGVzbywgVGFsbGEsIFpQRSwgWlRFLCB5IGNvbiB2YWxvcmVzIGNlcmNhbm9zIGFsIHByb21lZGlvIGdlbmVyYWwgcGFyYSBsYXMgdmFyaWFibGVzIEFsdHVyYVJFTiB5IFBUWi4gU2ludGV0aXphbmRvOiBzb24gbG9zIG1lbm9yZXMgZGUgbWF5b3IgZWRhZCwgZGUgbWF5b3IgcGVzbywgdGFsbGEgeSBoZW1vZ2xvYmluYSwgZGUgem9uYXMgZGUgYWx0dXJhIHByb21lZGlvLg0KLSBDbHVzdGVyIDI6IGNvbmZvcm1hZG8gcG9yIGxvcyBtw61uaW1vcyB2YWxvcmVzIGRlIGxhIHZhcmlhYmxlIGVkYWQsIElNQywgcGVzbywgUFRaLCBUYWxsYSwgWlBFLCBaVEU7IHkgY29uIHZhbG9yZXMgY2VyY2Fub3MgYWwgcHJvbWVkaW8gZ2VuZXJhbCBwYXJhIGxhIHZhcmlhYmxlcyBoZW1vZ2xvYmluYSB5IGhlbW9nbG9iaW5hIGFqdXN0YWRhOyBhdW5xdWUgZGUgbWF5b3IgYWx0dXJhUkVOLiBTaW50ZXRpemFuZG86IHNvbiBsb3MgbWVub3JlcyBkZSBtZW5vciBlZGFkLCBkZSBtZW5vciBwZXNvLCB0YWxsYSwgZGUgem9uYXMgZGUgbWF5b3IgYWx0dXJhIHkgaGVtb2dsb2JpbmEgcHJvbWVkaW8uDQotIENsdXN0ZXIgMzogY29uZm9ybWFkbyBwb3IgbG9zIG1lbm9yZXMgdmFsb3JlcyBlbiBsYSB2YXJpYWJsZSBhbHR1cmFSRU4sIGhlbW9nbG9iaW5hIHkgaGVtb2dsb2JpbmEgYWp1c3RhZGE7IHBlcm8gY29uIHZhbG9yZXMgcXVlIGZsdWN0dWFuIGFscmVkZWRvciBkZWwgcHJvbWVkaW8gZW4gbGFzIHZhcmlhYmxlcyBlZGFkLCBJTUMsIHBlc28sIHRhbGxhIHkgWlRFOyBjb24gdmFsb3JlcyBtw6F4aW1vcyBlbiBQVFogeSBaUEUuIFNpbnRldGl6YW5kbzogc29uIGxvcyBtZW5vcmVzIGRlIGVkYWQgcHJvbWVkaW8sIGNvbiBtZWRpZGFzIGFudHJvcG9tw6l0cmljYXMgcHJvbWVkaW8sIGRlIG1lbm9yIGhlbW9ibG9iaW5hIHkgYWx0dXJhUkVOLg0KDQoNCiMjIEFuw6FsaXNpcyBkZSBwZXJmaWxlcyBjb24gY2x1c3RlciBLLW1lYW5zIHNlZ8O6biBoZW1vZ2xvYmluYQ0KDQpUb21hbmRvIGxvcyByZXN1bHRhZG9zIGRlIEstbWVhbnMgc2UgYW5hbGl6YSBsYSB2YXJpYWJsZSBoZW1vZ2xvYmluYQ0KDQoNCmBgYHtyfQ0KZzFfRiA8LQ0KICBnZ3Bsb3QobXV0YXRlKGF5YWN1Y2hvX2ZpbmFsLCBjbHVzdGVyID0gZmFjdG9yKGNsdXN0ZXIpKSkgKw0KICBhZXMoY2x1c3RlciwgZmlsbCA9QW5lbWlhICkgKw0KICBnZW9tX2JhcihjbHVzdGVyID0gcG9zaXRpb25fZmlsbCgpKSArDQogIGxhYnModGl0bGU9Ikdyw6FmaWNhIGRlIENsdXN0ZXIgSy1tZWFucyBzZWfDum4gQW5lbWlhIiwNCiAgICAgICAgICAgIHggPSBOVUxMLCB5ID0gIlByb3BvcmNpw7NuIikgKw0KICB0aGVtZV9idygpDQpnMV9GDQpgYGANCg0KTGEgZ3LDoWZpY2EgbXVlc3RyYSBlbCBzaWd1aWVudGUgcGF0csOzbiBlbiBjdWFudG8gbGEgYW5lbWlhOg0KLSBDbHVzdGVyIDE6IENvbmZvcm1hZG8gcG9yIGxvcyBtZW5vcmVzIHF1ZSB0aWVuZW4gYW5lbWlhIGVuIG1heW9yIHByb3BvcmNpw7NuLCBjYXNpIGVsIDUwJSBkZWwgY2x1c3Rlci4NCi0gQ2x1c3RlciAyOiBDb25mb3JtYWRvIHBvciBsb3MgbWVub3JlcyBxdWUgdGllbmUgYW5lbWlhIGVuIGJhamEgcHJvcG9yY2nDs24gcGVybyBtYXlvciBhIGxhIGRlbCBjbHVzdGVyIDMuIA0KLSBDbHVzdGVyIDM6IENvbmZvcm1hZG8gcG9yIGxvcyBtZW5vcmVzIHF1ZSB0aWVuZSBhbmVtaWEgZW4gbGEgbcOhcyBiYWphIHByb3BvcmNpw7NuLg0KDQoNCg0KYGBge3J9DQpnMV9TIDwtDQogIGdncGxvdChtdXRhdGUoYXlhY3VjaG9fZmluYWwsIGNsdXN0ZXIgPSBmYWN0b3IoY2x1c3RlcikpKSArDQogIGFlcyhjbHVzdGVyLCBmaWxsID1TZXhvICkgKw0KICBnZW9tX2JhcihjbHVzdGVyID0gcG9zaXRpb25fZmlsbCgpKSArDQogIGxhYnModGl0bGU9Ikdyw6FmaWNhIGRlIENsdXN0ZXIgSy1tZWFucyBzZWfDum4gU2V4byIsDQogICAgICAgICAgICB4ID0gTlVMTCwgeSA9ICJQcm9wb3JjacOzbiIpICsNCiAgdGhlbWVfYncoKQ0KZzFfUw0KYGBgDQpBIG5pdmVsIGRlIGfDqW5lcm8gZGVsIG1lbm9yLCBzZSBvYnNlcnZhIHVuIGVxdWlsaWJyaW8gZW50cmUgbG9zIGNsw7pzdGVycy4NCg0KDQoNCiMgKipDb25jbHVzaW9uZXMqKg0KDQotIExhIGV2YWx1YWNpw7NuIGNvbiBsYSBmdW5jacOzbiBOYkNsdXN0IG5vcyBpbmRpY2EgcXVlIGRlYmVtb3MgdHJhYmFqYXIgY29uIDMgZ3J1cG9zLg0KDQotIExBIGV2YWx1YWNpw7NuIGRlIGxhIGNsdXN0ZXJpemFjacOzbiBwZXJtaXRlIGNvbmNsdWlyIHF1ZSBlbCBwZXJmaWxhZG8gZGVsIGFsZ29yaXRtbyBLbWVhbnMgZXMgZWwgbcOhcyBhZGVjdWFkbyBlbiBjb21wYXJhY2nDs24gY29uIGxvcyBvdHJvcyBhbGdvcmltb3MgdXNhZG9zLiANCg0KDQogIHwgIEFsZ29yaXRtbyAgfCBEYXZpZXMuQm91bGRpbiB8IER1bm4gICAgfCBTaWxob3VldHRlIHwNCiAgfDotLS0tLS0tLS0tLTp8Oi0tLS0tLS0tLS0tLS0tOnw6LS0tLS0tLS06fDotLS0tLS0tLS06fA0KICB8IEttZWFucyAgICAgIHwgMS43NCAgICAgICAgICAgfCAgMC4wMTAwICB8IDAuMTg0ICAgICB8DQogIHwgQWduZXMgICAgICAgfCAxLjgxICAgICAgICAgICB8ICAwLjAyODcgIHwgMC4xMjUgICAgIHwNCiAgfCBCaXJjaCAgICAgICB8IDIuMTcgICAgICAgICAgIHwgIDAuMDE3NCAgfCAwLjEzNCAgICAgfA0KDQoNCi0gRmluYWxtZW50ZSBzZSBvYnR1dmllcm9uIGxvcyBzaWd1aWVudGVzIGNsdXN0ZXJzIGNvbiBlbCBhbGdvcml0bW8gS21lYW5zOg0KRW4gY3VhbnRvIGEgbGFzIHZhcmlhYmxlcyBudW3DqXJpY2FzIGxhIGdyw6FmaWNhIG11ZXN0cmEgZWwgc2lndWllbnRlIHBhdHLDs246DQppKSBDbHVzdGVyIDE6IGNvbmZvcm1hZG8gcG9yIG3DrW5pbW9zIHZhbG9yZXMgZGUgbGEgdmFyaWFibGUgZWRhZCwgaGVtb2dsb2JpbmEsIGhlbW9nbG9iaW5hIGFqdXN0YWRhLCBJTUMsIFBlc28sIFRhbGxhLCB5IGNvbiB2YWxvcmVzIGNlcmNhbm9zIGFsIHByb21lZGlvIGdlbmVyYWwgcGFyYSBsYXMgdmFyaWFibGVzIFBUWiwgWlBFIHkgWlRFLiBTaW50ZXRpemFuZG86IHNvbiBsb3MgbWVub3JlcyBkZSBtZW5vciBlZGFkIChhbHJlZGVkb3IgZGUgdW4gYcOxbyksIG1lbm9yIHBlc28sIHRhbGxhIHkgaGVtb2dsb2JpbmEsIGRlIHpvbmFzIGRlIGFsdHVyYSBwcm9tZWRpby4gQWRlbcOhcywgdGllbmVuIGFuZW1pYSBlbiBtYXlvciBwcm9wb3JjacOzbiwgY2FzaSBlbCA1MCUgZGVsIGNsdXN0ZXIuDQppaSkgQ2x1c3RlciAyOiBjb25mb3JtYWRvIHBvciBsb3MgbcOheGltb3MgdmFsb3JlcyBkZSBsYSB2YXJpYWJsZSBJTUMsIHBlc28sIFBUWiwgVGFsbGEsIFpQRSwgWlRFOyB5IGNvbiB2YWxvcmVzIGNlcmNhbm9zIGFsIHByb21lZGlvIGdlbmVyYWwgcGFyYSBsYXMgdmFyaWFibGVzIGFsdHVyYSBSRU4sIGVkYWQgZW4gbWVzZXMsIGhlbW9nbG9iaW5hIHkgaGVtb2dsb2JpbmEgYWp1c3RhZGEuIFNpbnRldGl6YW5kbzogc29uIGxvcyBtZW5vcmVzIGRlIGVkYWQgY2VyY2FuYSBhbCBwcm9tZWRpbywgZGUgbWF5b3IgcGVzbyB5IHRhbGxhLCBkZSB6b25hcyBkZSBtZW5vciBhbHR1cmEsIGF1bnF1ZSBtdXkgY2VyY2FuYSBhbCBwcm9tZWRpbywgeSBjb24gaGVtb2dsb2JpbmEgcHJvbWVkaW8uIFRhbWJpw6luLCB0aWVuZW4gYW5lbWlhIGVuIGJhamEgcHJvcG9yY2nDs24gcGVybyBtYXlvciBhIGxhIGRlbCBjbHVzdGVyIDMuDQppaWkpIENsdXN0ZXIgMzogY29uZm9ybWFkbyBwb3IgbG9zIG3DoXhpbW9zIHZhbG9yZXMgZGUgbGEgdmFyaWFibGUgQWx0dXJhUkVOLCBlZGFkLCBoZW1vZ2xvYmluYSwgaGVtb2dsb2JpbmEgYWp1c3RhZGEgeSB0YWxsYTsgY29uIHZhbG9yZXMgY2VyY2Fub3MgYWwgcHJvbWVkaW8gZ2VuZXJhbCBwYXJhIGxhcyB2YXJpYWJsZXMgSU1DIHkgcGVzbywgcGVybyBjb24gdmFsb3JlcyBtw61uaW1vcyBwYXJhIGxhcyB2YXJpYWJsZXMgUFRaLCBaUEUgeSBaVEUuIFNpbnRldGl6YW5kbzogc29uIGxvcyBtZW5vcmVzIGRlIG1heW9yIGVkYWQsIG1heW9yIHRhbGxhLCBhdW5xdWUgY29uIHBlc28gcHJvbWVkaW8geSBkZSB6b25hcyBkZSBtYXlvciBhbHR1cmEgeSBtYXlvciBoZW1vZ2xvYmluYS4gSWd1YWxtZW50ZSwgdGllbmVuIGFuZW1pYSBlbiBsYSBtw6FzIGJhamEgcHJvcG9yY2nDs24NCg0KDQojICoqUmVmZXJlbmNpYXMqKg0KDQotIFsxXSBJTkVJLU1FRiAoMjAyMikuICJQZXLDujogSW5kaWNhZG9yZXMgZGUgcmVzdWx0YWRvcyBkZSBsb3MgcHJvZ3JhbWFzIHByZXN1cHVlc3RhbGVzLCAyMDE1LTIwMjDigJ0uIExpbWEsIFBlcsO6LiBDb25zdWx0YXIgZW46IGh0dHBzOi8vY29wZXJhaW5mYW5jaWFwZXJ1LmNvbS8yMDIyLzA4LzAxL3BlcnUtaW5kaWNhZG9yZXMtZGUtcmVzdWx0YWRvcy1kZS1sb3MtcHJvZ3JhbWFzLXByZXN1cHVlc3RhbGVzLTIwMTUtMjAyMC1lbmN1ZXN0YS1kZW1vZ3JhZmljYS15LWRlLXNhbHVkLWZhbWlsaWFyLw0KDQotIFsyXSBQbGF0YWZvcm1hIE5hY2lvbmFsIGRlIERhdG9zIEFiaWVydG9zICgyMDIyKS4gQ29uc3VsdGFyIGVuOiBodHRwczovL2RhdG9zLmlucy5nb2IucGUvZGF0YXNldC9zaXN0ZW1hLWRlLWluZm9ybWFjaW9uLWRlbC1lc3RhZG8tbnV0cmljaW9uYWwtZGUtbmlub3MteS1nZXN0YW50ZXMtcGVydS1pbnMtY2VuYW4tMjAxOS0yMDIwDQoNCi0gWzNdIE1pbnNhICgyMDE3KS4gIk5vcm1hIFTDqWNuaWNhICAtIE1hbmVqbyBUZXJhcMOpdXRpY28geSBwcmV2ZW50aXZvIGRlIGxhIGFuZW1pYSBlbiBuacOxb3MsIGFkb2xlc2NlbnRlcywgbXVqZXJlcyBnZXN0YW50ZXMgeSBwdcOpcnBlcmFzIi4gTGltYS1Qw6lydS4gQ29uc3VsdGFyIGVuOmh0dHBzOi8vd3d3LmdvYi5wZS9pbnN0aXR1Y2lvbi9taW5zYS9pbmZvcm1lcy1wdWJsaWNhY2lvbmVzLzI4MDg1NC1ub3JtYS10ZWNuaWNhLW1hbmVqby10ZXJhcGV1dGljby15LXByZXZlbnRpdm8tZGUtbGEtYW5lbWlhLWVuLW5pbm9zLWFkb2xlc2NlbnRlcy1tdWplcmVzLWdlc3RhbnRlcy15LXB1ZXJwZXJhcw0KDQotIFs0XSBPcmdhbml6YWNpw7NuIE11bmRpYWwgZGUgbGEgU2FsdWQgLSBPTVMgKDIwMTEpLiAiQ29uY2VudHJhY2lvbmVzIGRlIGhlbW9nbG9iaW5hIHBhcmEgZGlhZ25vc3RpY2FyIGxhIGFuZW1pYSB5IGV2YWx1YXIgc3UgZ3JhdmVkYWQiLiBHaW5lYnJhIChXSE8vTk1IL05IRC9NTk0vMTEuMSkuIENvbnN1bHRhciBlbjogaHR0cDovL3d3dy53aG8uaW50L3ZtbmlzL2luZGljYXRvcnMvaGFlbW9nbG9iaW5fZXMucGRmDQoNCi0gWzVdIEZhbm55IFJhbWFkaGFuaSBldCBhbCAoMjAyMCkuIOKAnEltcHJvdmUgQklSQ0ggYWxnb3JpdGhtIGZvciBiaWcgZGF0YSBjbHVzdGVyaW5n4oCdLCBJT1AgQ29uZi4gU2VyLjogTWF0ZXIuIFNjaS4gRW5nLiA3MjUgMDEyMDkwLCBNZWRhbiwgSW5kb25lc2lhLiBDb25zdWx0YXIgZW46IGh0dHBzOi8vaW9wc2NpZW5jZS5pb3Aub3JnL2FydGljbGUvMTAuMTA4OC8xNzU3LTg5OVgvNzI1LzEvMDEyMDkwIA0KDQotIFs2XSBBbG9uc28gZGVsIFNhc28sIEphdmllciAoMjAyMCkuIOKAnE3DqXRvZG9zIGRlIGRldGVjY2nDs24gZGUgYW5vbWFsw61hcyB5IGNsdXN0ZXJpbmcgZW4gc2VyaWVzIHRlbXBvcmFsZXPigJ0uIFNhbnRhbmRlciwgRXNwYcOxYS4gQ29uc3VsdGFyIGVuIDogaHR0cHM6Ly9yZXBvc2l0b3Jpby51bmljYW4uZXMveG1sdWkvaGFuZGxlLzEwOTAyLzIwNzgwIA0KDQo=