Librerías
Para \(k\)-means
El software R dispone de varias funciones de diferentes paquetes para llevar a cabo un análisis de conglomerados:
library(stats)
library(factoextra)
library(cluster)
Para otros análisis
library(aplore3) #Base de datos para los ejemplos
library(lsm) #Base de datos para ejemplos y estimaciones del Log-verosimilitud
library(tidyverse) #Incluye a dplyr y ggplot2
library(stringr) #Reemplazar caracteres en un data frame
library(outliers) #outliers::grubbs.test
library(EnvStats) #EnvStats::rosnerTest
library(DMwR2) #LOF (Local Outlier Factor)
library(rgl) #rgl::plot3d
library(corrplot) #Matriz de correlaciones
library(textshape) #column_to_rownames
library(openxlsx) #Librería para escribir archivos de Excel
library(mvtnorm) #Generar una distribución aleatoria normal multivariante
library(caret) #caret::confusionMatrix
library(knitr) #crear tablas con estilo
library(kableExtra) #crear tablas con estilo, pero para html
Algoritmo de Hartigan-Wong
\(k\)-means: pasos del algoritmo
El algoritmo \(k\)-means puede resumirse así:
1. Inicialización.
Especificar la cantidad de grupos (\(k\)) que se crearán (determinado por el analista).
Seleccionar aleatoriamente \(k\) objetos del conjunto de datos como los centroides iniciales (medias) de los grupos.
El centroide de un grupo es un vector de longitud \(p\) que contiene las medias de todas las variables para las observaciones en ese grupo, donde \(p\) es el número de variables.
2. Asignación de Clústeres.
Asignar cada observación a su centroide más cercano, basándose en la distancia euclidiana entre el objeto y el centroide.
Esta etapa se conoce como paso de asignación de grupos.
Es importante destacar que, para emplear la distancia de correlación, los datos se ingresan como puntuaciones \(Z\).
3. Actualización de Centroides.
Para cada uno de los \(k\) grupos, actualizar el centroide del grupo calculando los nuevos valores medios de todos los puntos de datos en el grupo.
Se utiliza el término actualización del centroide del grupo para describir esta fase.
Ahora que los centroides han sido recalculados, cada observación se revisa nuevamente para determinar si podría estar más cercana a otro grupo. Todos los objetos se reasignan utilizando los centroides de grupo actualizados.
4. Iteración.
Repetir los pasos de asignación y actualización hasta que la asignación de puntos a los clústeres no cambie significativamente entre iteraciones o hasta que se alcance un número máximo de iteraciones.
La idea es minimizar de manera iterativa la suma total de cuadrados dentro de los grupos, es decir, iterar los pasos 3 y 4 hasta que las asignaciones de grupo dejen de cambiar o se alcance el número máximo de iteraciones.
Por defecto, el software R
utiliza 10 como valor predeterminado para el número máximo de iteraciones.
5. Regla de Hartigan.
- En cada iteración, evaluar si mover un punto de su clúster actual a otro clúster reduce la suma total de cuadrados intra-clúster. Si es así, hacer el movimiento y actualizar los centroides.
6. Covergencia.
Los pasos de asignación de grupos y actualización de centroides se repiten de manera iterativa hasta que las asignaciones de grupo dejen de cambiar, es decir, hasta que se logre la convergencia.
El algoritmo converge cuando ya no se producen cambios significativos en la asignación de puntos a los clústeres o la suma de cuadrados intra-clúster no puede reducirse más.
Esto implica que los grupos formados en la iteración actual son los mismos que los obtenidos en la iteración anterior.
\(k\)-means: sumas de cuadrados
1. \(\text{Withinss}_k\) (Suma de cuadrados dentro del clúster \(k\)).
El llamado vector de suma de cuadrados dentro del clúster \(k\) es definido por:
\[\text{Withinss}_k \,=\; W(C_k) \;=\; \sum\limits_{x_i \in C_k} (x_i -\overline{x}_k)^2\;=\; \sum\limits_{x_i \in C_k} d_i^2\]
2. \(\text{Withinss}_k\) (notaciones).
En la fórmula anterior:
\(x_i\) representa un punto de datos que pertenece al grupo \(C_k\).
\(\overline{x}_k\) representa la media de los puntos asignados al grupo \(C_k\).
\(d_i = x_i - \overline{x}_k\) la distancia del punto \(x_i\) al centroide \(\overline{x}_k\), dentro del clúster \(C_k\).
3. \(\text{Withinss}_k\) (gráfico).
Véase la figura 4.1.
4. \(\text{Withinss}_k\) (asignación).
Cada observación (\(x_i\)) se asigna a un clúster específico de modo que la suma de los cuadrados (SS) de las distancias entre la observación y los centros de clúster asignados \(\overline{x}_k\) sea la menor posible.
5. \(\text{Tot.withinss}\) (variabilidad total dentro de todos los clúster).
La suma total de cuadrados dentro de todos los clústers es una medida de qué tan compacto es el agrupamiento (es decir, su calidad).
Representa la variabilidad total dentro de todos los clusters.
Un valor mayor de \(\text{Tot.withinss}\) indica que los puntos dentro de cada cluster están más dispersos, lo que sugiere una menor calidad de clustering.
Se define de la siguiente manera:
\[\text{Tot.withinss} \,=\;\sum\limits_{k=1}^K W(C_k) \;=\; \sum\limits_{k=1}^K\sum\limits_{x_i \in C_k} (x_i -\overline{x}_k)^2\;=\; \sum\limits_{k=1}^K\sum\limits_{x_i \in C_k} d_i^2\]
6. \(\text{Tot.withinss}\) (gráfico).
Véase la figura 4.2.
8. Objetivo final.
Nuestro objetivo es minimizar esta suma tanto como sea posible:
\[\min(\text{Tot.withinss}) \,=\;\min \sum\limits_{k=1}^K W(C_k)\]
\(k\)-means: proporción de varianza explicada
1. \(\text{Totss}\) (variabilidad total en todos los datos).
La suma total de cuadrados mide la variabilidad total en los datos y mide la variabilidad total en todos los datos (sin particionar los datos originales, como si solo tuviésemos un solo clúster).
2. \(\text{Totss}\) (definición).
La suma total de cuadrados se define como:
\[\text{Totss} \; =\; \sum\limits_{i=1}^n (x_i - \overline{x})^2\]
3. \(\text{Betweenss}\) (suma de cuadrados entre clústeres).
La suma de cuadrados entre clústeres, que mide la variación debido a las diferencias entre los centroides de los clústeres, se define como:
\[\text{Betweenss} \; = \; \text{Totss} \,-\, \text{Tot.withinss}\]
4. \(\text{Prop.Var}\) (proporción de varianza total).
La proporción de varianza total en los datos que es explicada por la variación entre los centroides de los clústeres (que es explicada por la agrupación de los datos en los clústeres) se calcula así:
\[\text{Prop.Var}\; = \;\frac{\text{Betweenss} }{\text{Totss}} \; = \; 1\;-\; \frac{\text{Tot.withinss}}{\text{Totss}}\]
5. \(\text{Prop.Var}\) (interpretación).
La interpretación de la proporción de varianza explicada es como sigue:
Un valor más alto (cercano a 100%) indica que los clústeres formados explican bien la variación total en los datos, sugiriendo que los clústeres están bien definidos y separados.
Un valor más bajo sugiere que los clústeres no explican bien la variación en los datos, lo que podría indicar que los clústeres no están bien definidos o que no hay una estructura clara en los datos.
Ejemplo: datos
Base de datos
Los datos se recogieron aplicando una encuesta a una muestra de estudiantes universitarios. Es un data frame con 800 observaciones y 66 variables. Con estos datos llevaremos a cabo un PCA.
datosCompleto <- lsm::survey
#datosCompleto <- textshape::column_to_rownames(dat, loc=1)
#datosCompleto %>% remove_rownames %>% column_to_rownames(var="names") #library(tidyverse)
attach(datosCompleto)
names(datosCompleto)
## [1] "Observation" "ID" "Gender" "Like" "Age"
## [6] "Smoke" "Height" "Weight" "BMI" "School"
## [11] "SES" "Enrollment" "Score" "MotherHeight" "MotherAge"
## [16] "MotherCHD" "FatherHeight" "FatherAge" "FatherCHD" "Status"
## [21] "SemAcum" "Exam1" "Exam2" "Exam3" "Exam4"
## [26] "ExamAcum" "Definitive" "Expense" "Income" "Gas"
## [31] "Course" "Law" "Economic" "Race" "Region"
## [36] "EMO1" "EMO2" "EMO3" "EMO4" "EMO5"
## [41] "GOAL1" "GOAL2" "GOAL3" "Pre_STAT1" "Pre_STAT2"
## [46] "Pre_STAT3" "Pre_STAT4" "Post_STAT1" "Post_STAT2" "Post_STAT3"
## [51] "Post_STAT4" "Pre_IDARE1" "Pre_IDARE2" "Pre_IDARE3" "Pre_IDARE4"
## [56] "Pre_IDARE5" "Post_IDARE1" "Post_IDARE2" "Post_IDARE3" "Post_IDARE4"
## [61] "Post_IDARE5" "PSICO1" "PSICO2" "PSICO3" "PSICO4"
## [66] "PSICO5"
Solo datos numéricos
Solo utilizaremos algunas variables numéricas. Los datos deben contener solo variables continuas, ya que el algoritmo \(k\)-means utiliza medias variables. Dado que no queremos que el algoritmo \(k\)-means dependa de una unidad de variable arbitraria, comenzamos escalando los datos utilizando la función scale
de R de la siguiente manera:
dat <- datosCompleto[1:100, 21:24]
df <- scale(dat)
head(df)
## SemAcum Exam1 Exam2 Exam3
## [1,] 1.4571897 -1.68245049 1.6460816 1.9350576
## [2,] -0.9397758 -0.94735470 1.5482171 0.5559047
## [3,] 1.2918817 0.06340201 0.2759780 -1.2476029
## [4,] -0.2785439 -0.76358075 0.8631653 1.9350576
## [5,] 0.1347260 -0.21225891 0.1781135 1.9350576
## [6,] -1.0224297 0.43094991 1.0588944 1.0863481
Estimando el número óptimo de clústers.
Pregunta central
El proceso de agrupamiento \(k\)-means requiere que los usuarios indiquen cuántos grupos desean generar. Una pregunta central es: ¿Cómo seleccionar el número adecuado de grupos (\(k\))?
Respuesta a pregunta central
Consiste en realizar el agrupamiento \(k\)-means utilizando diferentes valores de \(k\).
Luego, se grafica la suma de los cuadrados internos (wss) en función del número de grupos.
Normalmente, la ubicación de un quiebre (punto de inflexión) en el gráfico se considera como un indicador del número apropiado de grupos.
Con R
La función fviz_nbclust
del paquete factoextra
brinda una solución práctica para estimar el número óptimo de grupos.
library(factoextra)
fviz_nbclust(df, kmeans, method = "wss") +
geom_vline(xintercept = 4, linetype = 2)

El gráfico ilustra cómo la variabilidad dentro de los grupos cambia con el número de grupos, \(k\). Esta variabilidad disminuye con \(k\), pero se nota un punto de inflexión o “codo” en \(k = 4\). Este punto sugiere que agregar más grupos después del cuarto no aporta mucho valor. En la siguiente sección, procederemos a clasificar las observaciones en 4 grupos.
Calculando el agrupamiento \(k\)-means
set.seed
El algoritmo de agrupamiento \(k\)-means comienza seleccionando k centroides de manera aleatoria, por lo que es recomendable usar la función set.seed
para fijar una semilla en el generador de números aleatorios de R.
Esto garantiza que los resultados sean reproducibles, de modo que cualquier lector de este artículo obtenga los mismos resultados que se muestran a continuación.
kmeans
El código R a continuación ejecuta el agrupamiento \(k\)-means con \(k = 4\):
#Calcular k-means con k = 4
set.seed(123)
k <-4
km.res <- kmeans(df, centers = k, nstart = 25)
nstart
Debido a que el resultado final del agrupamiento \(k\)-means depende de las asignaciones iniciales aleatorias, especificamos nstart = 25
.
Esto significa que R probará 25 asignaciones iniciales aleatorias diferentes y seleccionará los mejores resultados basados en la variación intra-cluster más baja.
Aunque el valor predeterminado de nstart
en R es uno, es altamente recomendable utilizar un valor más grande, como 25 o 50, para obtener resultados más estables.
Resultados finales
print(km.res)
## K-means clustering with 4 clusters of sizes 21, 27, 22, 30
##
## Cluster means:
## SemAcum Exam1 Exam2 Exam3
## 1 1.21709954 0.1027821 -0.8051922 0.1669129
## 2 0.01839816 -0.4334683 0.9320329 0.8663123
## 3 -0.71811281 -0.8930579 -0.6759771 -0.1818939
## 4 -0.34191197 0.9730831 0.2205214 -0.7631313
##
## Clustering vector:
## [1] 2 2 1 2 2 2 4 1 1 3 2 3 2 3 1 3 3 1 3 2 2 2 2 2 3 4 4 3 3 2 1 3 1 1 3 4 4
## [38] 1 4 1 2 4 1 4 3 2 1 4 4 2 4 2 4 1 4 2 3 4 4 3 2 4 4 2 2 4 2 4 1 2 4 3 3 4
## [75] 1 1 3 4 3 4 3 4 4 3 4 1 1 4 3 4 1 4 2 4 1 2 1 3 2 2
##
## Within cluster sum of squares by cluster:
## [1] 53.07428 57.81572 39.62427 60.63989
## (between_SS / total_SS = 46.7 %)
##
## Available components:
##
## [1] "cluster" "centers" "totss" "withinss" "tot.withinss"
## [6] "betweenss" "size" "iter" "ifault"
Interpretaciones
La salida impresa presenta:
Los promedios o centros de los grupos: una matriz en la que las filas representan el número del grupo (1 a 4) y las columnas representan las variables.
El vector de agrupamiento: Un conjunto de números enteros (de 1 a \(k\)) que señala el grupo al que se asigna cada punto.
Clasificar observaciones por clústers
Si se desea agregar las clasificaciones de los puntos a los datos originales, se puede ejecutar este código:
df.clasif <- cbind(dat, cluster = km.res$cluster)
head(df.clasif)
## SemAcum Exam1 Exam2 Exam3 cluster
## 1 4.25 1.5 5.0 5.0 2
## 2 2.80 2.3 4.9 3.7 2
## 3 4.15 3.4 3.6 2.0 1
## 4 3.20 2.5 4.2 5.0 2
## 5 3.45 3.1 3.5 5.0 2
## 6 2.75 3.8 4.4 4.2 2
Output de kmeans
kmeans
: valores que se pueden obtener
La función kmeans
devuelve una lista de componentes que incluyen:
cluster
: Un vector de enteros (de 1 a \(k\)) que indica el clúster al que se asigna cada punto.
centers
: Una matriz de centros de clúster (medias de clúster).
totss
: La suma total de cuadrados y mide la variabilidad total en todos los datos (sin particionar los datos originales, como si solo tuviésemos un solo clúster). Es decir,
\[\text{Totss} \; =\; \sum\limits_{i=1}^n(x_i - \overline{x})^2\]
withinss
: Vector de suma de cuadrados dentro del clúster, con un componente por clúster. Es decir,
\[\text{Withinss}_k \,=\; W(C_k) \;=\; \sum\limits_{x_i \in C_k} (x_i -\overline{x}_k)^2\]
tot.withinss
: Suma total de cuadrados dentro del clúster, es decir,
\[\text{Tot.withinss} \,=\;\sum\limits_{k=1}^K W(C_k) \;=\; \sum\limits_{k=1}^K\sum\limits_{x_i \in C_k} (x_i -\overline{x}_k)^2\]
betweenss
: La suma de cuadrados entre clústeres, es decir,
\[\text{Betweenss} \; = \; \text{Totss} \,-\, \text{Tot.withinss}\]
size
: El número de observaciones en cada clúster.
kmeans
: con nuestros datos
Algunos de estos componentes pueden ser accedidos de la siguiente manera:
size
: El número de observaciones \(n_k\) en cada clúster \(k\). Observe:
\[n\;=\; \sum\limits_{k=1}^K n_k \]
km.res$size
## [1] 21 27 22 30
centers
: Una matriz de centros de clúster (medias de clúster).
km.res$centers
## SemAcum Exam1 Exam2 Exam3
## 1 1.21709954 0.1027821 -0.8051922 0.1669129
## 2 0.01839816 -0.4334683 0.9320329 0.8663123
## 3 -0.71811281 -0.8930579 -0.6759771 -0.1818939
## 4 -0.34191197 0.9730831 0.2205214 -0.7631313
cluster
: Un vector de enteros (de 1 a \(k\)) que indica el clúster al que se asigna cada punto.
km.res$cluster
## [1] 2 2 1 2 2 2 4 1 1 3 2 3 2 3 1 3 3 1 3 2 2 2 2 2 3 4 4 3 3 2 1 3 1 1 3 4 4
## [38] 1 4 1 2 4 1 4 3 2 1 4 4 2 4 2 4 1 4 2 3 4 4 3 2 4 4 2 2 4 2 4 1 2 4 3 3 4
## [75] 1 1 3 4 3 4 3 4 4 3 4 1 1 4 3 4 1 4 2 4 1 2 1 3 2 2
totss
: La suma total de cuadrados, que mide la variabilidad total en todos los datos (sin particionar los datos originales, como si solo tuviésemos un solo clúster). Es decir,
\[\text{Totss} \; =\; \sum\limits_{i=1}^n (x_i - \overline{x})^2\]
km.res$totss
## [1] 396
withinss
: Vector de suma de cuadrados dentro del clúster, con un componente por clúster. Es decir,
\[\text{Withinss} \,=\; W(C_k) \;=\; \sum\limits_{x_i \in C_k} (x_i -\overline{x}_k)^2\]
km.res$withinss
## [1] 53.07428 57.81572 39.62427 60.63989
tot.withinss
: Suma total de cuadrados dentro del clúster, es decir,
\[\text{Tot.withinss} \,=\;\sum\limits_{k=1}^K W(C_k) \;=\; \sum\limits_{k=1}^K\sum\limits_{x_i \in C_k} (x_i -\overline{x}_k)^2\]
km.res$tot.withinss
## [1] 211.1542
betweenss
: La suma de cuadrados entre clústeres, que mide la variación debido a las diferencias entre los centroides de los clústeres. Es decir,
\[\text{Betweenss} \; = \; \text{Totss} \,-\, \text{Tot.withinss}\]
km.res$betweenss
## [1] 184.8458
kmeans
: proporción de varianza explicada
La proporción de varianza total explicada por la variación entre los centroides de los clústeres se puede calcular así:
\[\text{Prop.Var}\; = \;\frac{\text{Betweenss} }{\text{Totss}} \; = \; 1\;-\; \frac{\text{Tot.withinss}}{\text{Totss}}\]
En nuestro ejemplo:
\[\text{Prop.Var}\; = \;\frac{\text{Betweenss} }{\text{Totss}}\times 100 \,\% \; = \; \frac{184.8458}{396}\times 100 \,\% \; = \; 46.68\,\%\]
Se resalta que arriba se calculó el porcentaje de varianza total explicada por la variación entre los centroides de los clústeres. En R se puede calcular como se indica abajo. :
(km.res$betweenss/km.res$totss)*100
## [1] 46.67824
Visualizando los clústers \(k\)-means
Introducción
Es recomendable graficar los resultados de los clústeres, ya que esto permite evaluar la elección del número de clústeres y comparar distintos análisis de clústeres.
Ahora deseamos visualizar los datos en un gráfico de dispersión, coloreando cada punto según su asignación al clúster correspondiente.
El reto surge cuando los datos tienen más de dos variables, lo que plantea la pregunta de qué variables seleccionar para el gráfico de dispersión en los ejes x e y.
Una solución es aplicar un algoritmo de reducción de dimensionalidad, como el Análisis de Componentes Principales (PCA), que transforma las cuatro variables en dos nuevas variables (que representan a las originales) que pueden usarse para el gráfico.
En otras palabras, si disponemos de un conjunto de datos multidimensional, una solución es realizar un Análisis de Componentes Principales (PCA) y graficar los puntos de datos según las coordenadas de los dos primeros componentes principales.
Aplicando factorextra::fviz_cluster
La función fviz_cluster
del paquete factoextra
se puede utilizar para visualizar fácilmente los clústeres k-means.
Esta función toma los resultados de k-means y los datos originales como argumentos.
En el gráfico resultante, las observaciones se representan mediante puntos, utilizando componentes principales si el número de variables es superior a 2.
fviz_cluster(km.res, data=df,
palette = c("blue", "darkorange4", "red", "magenta4"),
xlab = FALSE,
ylab = FALSE,
geom="point")

- También es posible dibujar una elipse de concentración alrededor de cada clúster.
fviz_cluster(km.res, data = df,
palette = c("blue", "darkorange4", "red", "magenta4"),
ellipse.type = "euclid", # Elipse de concentración
star.plot = TRUE, # Segmentos desde centroide a ítems
repel = TRUE, # Evitar traslapamientos
ggtheme = theme_minimal()
)

Ejercicios
Ejercicio 1
Analicemos el siguiente conjunto de datos. Para su creación, generamos tres clusters utilizando distribuciones normales. Cada punto de datos incluye tres características: un valor x
de una variable \(X\) , un valor y
de \(Y\), y una clase
que etiqueta el punto. Existen tres clases en este conjunto de datos (1, 2 y 3). El objetivo del ejercicio es que los algoritmos de clustering representen estas clases como clusters.
library(mvtnorm)
set.seed(123)
dataset <- {
#crear las 3 distribuciones
cluster1 = data.frame(rmvnorm(40, c(0, 0), diag(2)*c(2, 1))) %>% mutate(class=factor(1))
cluster2 = data.frame(rmvnorm(100, c(3, 3), diag(2)*c(1, 3))) %>% mutate(class=factor(2))
cluster3 = data.frame(rmvnorm(60, c(6, 6), diag(2))) %>% mutate(class=factor(3))
#crear el data frame
data = bind_rows(cluster1, cluster2, cluster3)
#nombres de las columnas
names(data) = c("x", "y", "clase")
#retornar los datos
data
}
head(dataset)
## x y clase
## 1 -0.7926323 -0.23017749 1
## 2 2.2043464 0.07050839 1
## 3 0.1828405 1.71506499 1
## 4 0.6518339 -1.26506123 1
## 5 -0.9713566 -0.44566197 1
## 6 1.7311131 0.35981383 1
Para ello, realizar los siguientes incisos.
Graficar los datos en un diagrama de dispersión usando ggplot
y el argumento color=class
para observar la agrupación natural que se intenta replicar.
Aplicar la función fviz_nbclust
del paquete factoextra
para estimar el número óptimo de grupos.
Defina un nuevo objeto que contenga solo las variables x
y y
.
df1 <- dataset[c("x", "y")]
head(df1)
## x y
## 1 -0.7926323 -0.23017749
## 2 2.2043464 0.07050839
## 3 0.1828405 1.71506499
## 4 0.6518339 -1.26506123
## 5 -0.9713566 -0.44566197
## 6 1.7311131 0.35981383
Ejecutar el agrupamiento \(k\)-means con el número optimal de clústers obtenidos en el inciso anterior (utilizar nstar=20
). LLame al nuevo objeto km.res1
.
Utilizar la función fviz_cluster
para ver los resultados de la agrupación \(k\)-means.
Ejercicio 2
Continuación del ejercicio 1. Considere la situación planteada en el ejercicio 1 y los resultados encontrados allí.
- Considere el código de abajo. En la línea de código No. 1, se copia el dataset original
dataset
como un nuevo dataframe llamado dataset_km
. En la línea de código No. 2, se añade una nueva columna llamada clase_pred
al nuevo data frame dataset_km
. Esta columna se rellena con los valores de las etiquetas de clústeres predichos por el modelo de \(k\)-means (km.res1$cluster
). Los valores se convierten en factores (categóricos) usando la función factor
. Compare los valores observados (clase
) con los predichos (clase_pred
).
#1.Copiar el dataset original
dataset_km <- dataset
#2. Añade una columna con las clases predichas
dataset_km['clase_pred'] = factor(km.res1$cluster)
head(dataset_km)
Contruir una tabla de frecuencias agrupadas entre los valores observados (clase
) con los predichos (clase_pred
) e interprete cada uno de los resultados encontrados en las celdas. En particular, verificar la precisión (accuracy) de la agrupación.
Verificar la precisión (accuracy) de la agrupación en un gráfico. Compare nuevamente los valores observados (clase
) con los predichos (clase_pred
). Puede utilizar el siguiente código:
#3. El gráfico
dataset_km %>% ggplot(aes(x=x, y=y, shape=clase, color=clase_pred)) +
geom_point() +
coord_fixed() +
scale_shape_manual(values=c(0, 1, 2)) +
scale_shape(solid = TRUE)
Ejercicio 3
Continuación de los ejercicios 1 y 2. Considere la situación planteada en el ejercicio 1 y los resultados encontrados en ese ejercicio y en el 2.
Inciso i.
Investigue el concepto de matrix de confusión y las métricas que se obtienen a partir de ella. Explique las más importantes. Puede consultar los siguientes documentos (de mi autoría):
inciso j.
Aplique el código de abajo para obtener la matrix de confusión y las métricas correspondientes. Interprete los resultados obtenidos.
library(caret)
conf_mat <- confusionMatrix(factor(km.res1$cluster), factor(dataset$clase),
mode = "everything", positive="1")
conf_mat
LS0tDQp0aXRsZTogIkFOw4FMSVNJUyBERSBDT05HTE9NRVJBRE9TIg0Kc3VidGl0bGU6IDxoMT4qKkFsZ29yaXRtbyAkayQtbWVhbnMqKjwvaDE+DQoNCmF1dGhvcjogDQogIC0gbmFtZSAgICAgICAgICA6ICJEci4gcmVyLiBuYXQuIEh1bWJlcnRvIExMaW7DoXMgU29sYW5vIg0KICAgIGFmZmlsaWF0aW9uICAgOiAiRGVwYXJ0YW1lbnRvIGRlIE1hdGVtw6F0aWNhcyB5IEVzdGFkw61zdGljYSwgVW5pdmVyc2lkYWQgZGVsIE5vcnRlIChCYXJyYW5xdWlsbGEsIENvbG9tYmlhKSINCiAgICAgI2NvcnJlc3BvbmRpbmcgOiB5ZXMgICAgIyBEZWZpbmUgb25seSBvbmUgY29ycmVzcG9uZGluZyBhdXRob3INCiAgICAgI2FkZHJlc3MgICAgICAgOiAiRGVwYXJ0YW1lbnRvIGRlIE1hdGVtw6F0aWNhcyB5IEVzdGFkw61zdGljYSINCiAgICBlbWFpbCAgICAgICAgIDogfA0KICAgICAgaGxsaW5hc0B1bmlub3J0ZS5lZHUuY28NCiAgICAgIA0KICAgICAgW0Jpb2dyYXBoaWNhbCBza2V0Y2hdKGh0dHBzOi8vcnB1YnMuY29tL2hsbGluYXMvQmlvX1NrZXRjaCkNCiAgICAgIA0KICAgICAgYHIgZm9ybWF0KFN5cy50aW1lKCksICIlZC8lbS8leSIpYCANCiAgICAgIA0KICAgICAjcm9sZTogICAgICAgICAjIENvbnRyaWJ1dG9yc2hpcCByb2xlcyAoZS5nLiwgQ1JlZGlULCBodHRwczovL2Nhc3JhaS5vcmcvY3JlZGl0LykNCiAgIyAgICAtIENvbmNlcHR1YWxpemF0aW9uDQogICMgICAgLSBXcml0aW5nIC0gT3JpZ2luYWwgRHJhZnQgUHJlcGFyYXRpb24NCiAgIyAgICAtIFdyaXRpbmcgLSBSZXZpZXcgJiBFZGl0aW5nDQogIyAtIG5hbWUgICAgICAgICAgOiAiQXV0b3IgbnVtZXJvIDIiDQogIyAgIGFmZmlsaWF0aW9uICAgOiAiMSwyIg0KICMgICByb2xlOg0KICMgICAgIC0gV3JpdGluZyAtIFJldmlldyAmIEVkaXRpbmcNCiAgICAgI2FmZmlsaWF0aW9uOg0KICAjLSBpZCAgICAgICAgICAgIDogIjEiDQogICMgIGluc3RpdHV0aW9uICAgOiAiVW5pdmVyc2lkYWQgZGVsIE5vcnRlIChCYXJyYW5xdWlsbGEsIENvbG9tYmlhKSINCiAgIyFbXShobGxpbmFzLmpwZyl7d2lkdGg9MWlufSANCiAgDQojZGF0ZTogJ2ByIGZvcm1hdChTeXMudGltZSgpLCAiJWQvJW0vJXkiKWAnICAjIHZlciBodHRwczovL2Jvb2tkb3duLm9yZy95aWh1aS9ybWFya2Rvd24tY29va2Jvb2svdXBkYXRlLWRhdGUuaHRtbA0Kb3V0cHV0OiANCiAgICBib29rZG93bjo6aHRtbF9kb2N1bWVudDI6IA0KICAgICAgICAgICNPSk8gU2FsZW4gY2FwaXR1bG9zLCBzZWNjaW9uZXMgeSBUZW9yZW1hcw0KICAgICNib29rZG93bjo6aHRtbF9ib29rOg0KICAgICAgICAgICNPSk8gRVJST1IgU2FsZW4gdGVvcmVtYXMsIHBlcm8gbm8gc2FsZW4gbG9zIGNhcGl0dWxvcyANCiAgICAjaHRtbF9kb2N1bWVudDoNCiAgICAgICAgICB0b2M6IHRydWUgICAgICAjIHRhYmxlIG9mIGNvbnRlbnQgdHJ1ZQ0KICAgICAgICAgIHRvY19kZXB0aDogNCAgICMgdXB0byB0aHJlZSBkZXB0aHMgb2YgaGVhZGluZ3MgKHNwZWNpZmllZCBieSAjLCAjIyBhbmQgIyMjKQ0KICAgICAgICAgIHRvY19mbG9hdDogdHJ1ZSAjQ29uIHRydWUsIHRvYyBzYWxlIGFsIG1hcmdlbiBpenF1aWVyZG8gZGUgbGEgcMOhZ2luYTsgZGUgbG8gY29udHJhcmlvLCBhcnJpYmENCiAgICAgICAgICBjb2xsYXBzZWQ6IGZhbHNlDQogICAgICAgICAgc21vb3RoX3Njcm9sbDogZmFsc2UNCiAgICAgICAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUgICAjIGlmIHlvdSB3YW50IG51bWJlciBzZWN0aW9ucyBhdCBlYWNoIHRhYmxlIGhlYWRlcg0KICAgICAgICAgICN0aGVtZTogc2FuZHN0b25lDQogICAgICAgICAgI3RoZW1lOiB1bml0ZWQgICMgbWFueSBvcHRpb25zIGZvciB0aGVtZSwgdGhpcyBvbmUgaXMgbXkgZmF2b3JpdGUuDQogICAgICAgICAgI3RoZW1lOiBmbGF0bHkgICMgDQogICAgICAgICAgI3RoZW1lOiBjZXJ1bGVhbiAgIyANCiAgICAgICAgICAjaGlnaGxpZ2h0OiB0YW5nbyAgIyBzcGVjaWZpZXMgdGhlIHN5bnRheCBoaWdobGlnaHRpbmcgc3R5bGUNCiAgICAgICAgICAjY3NzOiBTY3JpcHRzIGFjY2Vzb3Jpb3MvZXN0aWxvYm90b24uY3NzDQogICAgICAgICAgI2NzczogbXkuY3NzICAgIyB5b3UgY2FuIGFkZCB5b3VyIGN1c3RvbSBjc3MsIHNob3VsZCBiZSBpbiBzYW1lIGZvbGRlcg0KICAgICAgICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgICAgICAgICAjaGlnaGxpZ2h0OiB0YW5nbyAgIyBjYW1iaWFyIGNvbG9yIGRlIGxpYnJhcnkgZW4gYXp1bA0KICAgICMgYm9va2Rvd246OmdpdGJvb2s6DQogICAgIyAgICAgIGluY2x1ZGVzOg0KICAgICMgICAgICAgIGluX2hlYWRlcjogaGVhZGVyLmh0bWwNCiAgICAjIGJvb2tkb3duOjpwZGZfYm9vazoNCiAgICAjICAgICAgIGtlZXBfdGV4OiB5ZXMNCiAgICAjIGJvb2tkb3duOjpodG1sX2Jvb2s6DQogICAgIyAgICAgICBjc3M6IHRvYy5jc3MNCiAgICAjIGJvb2tkb3duOjpodG1sX2Jvb2s6DQogICAgIyAgICAgICAgIGluY2x1ZGVzOg0KICAgICMgICAgICAgICAgIGluX2hlYWRlcjogc3R5bGUuY3NzDQogICAgI2Jvb2tkb3duOjpodG1sX2RvY3VtZW50MjogZGVmYXVsdA0KICAgICMgYm9va2Rvd246OnBkZl9kb2N1bWVudDI6DQogICAgIyAgICAgIGtlZXBfdGV4OiB0cnVlDQogICAgI2JpYmxpb2dyYXBoeTogcmVmZXJlbmNlcy5iaWINCiAgICBtYXRoamF4OiAiaHR0cDovL2V4YW1wbGUuY29tL21hdGhqYXgvTWF0aEpheC5qcz9jb25maWc9VGVYLUFNUy1NTUxfSFRNTG9yTU1MIg0KaGVhZGVyLWluY2x1ZGVzOg0KICAgIFx1c2VwYWNrYWdlW3gxMW5hbWVzXXt4Y29sb3J9IA0KICAgIA0KY3NsOiBzY2llbmNlLmNzbA0KI09qbzogU2UgdXRpbGl6YSBsZW5ndWFqZSBZQU1MDQoNCmFic3RyYWN0OiB8DQogKipFbiBbUnB1YnM6OiB0b2NdKGh0dHBzOi8vcnB1YnMuY29tL2hsbGluYXMvdG9jKSBzZSBwdWVkZW4gdmVyIG90cm9zIGRvY3VtZW50b3MgZGUgcG9zaWJsZSBpbnRlcsOpcy4qKg0KICANCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgZmlnLmFsaWduPSJjZW50ZXIiLCAgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSMsDQogICAgICAgICAgICAgICAgICAgICAgI3N0eWxlID0gImNvbG9yOmRhcmtibHVlIg0KICAgICAgICAgICAgICAgICAgICAjIGNsYXNzLnNvdXJjZT0iYmctZGFuZ2VyIiwgY2xhc3Mub3V0cHV0PSJiZy13YXJuaW5nIiAgICNDb2xvcmVzIGRlbnRybyBkZWwgY2h1bmsNCiAgICAgICAgICAgICAgICAgICAgICkNCmxpYnJhcnkocmdsKQ0Ka25pdHI6OmtuaXRfaG9va3Mkc2V0KHdlYmdsID0gaG9va193ZWJnbCkNCmBgYA0KDQoNCg0KDQpgYGB7ciwgZWNobz1GQUxTRSwgZXZhbD1GQUxTRX0NCmh0dHBzOi8vYm9va2Rvd24ub3JnL3lpaHVpL3JtYXJrZG93bi9sYW5ndWFnZS1lbmdpbmVzLmh0bWwNCg0KaHR0cHM6Ly9ib29rZG93bi5vcmcveWlodWkvYm9va2Rvd24vbWFya2Rvd24tc3ludGF4Lmh0bWwNCg0KaHR0cHM6Ly9ib29rZG93bi5vcmcveWlodWkvYm9va2Rvd24vYS1zaW5nbGUtZG9jdW1lbnQuaHRtbA0KDQpodHRwczovL2Jvb2tkb3duLm9yZy95aWh1aS9ib29rZG93bi9tYXJrZG93bi1leHRlbnNpb25zLWJ5LWJvb2tkb3duLmh0bWwNCg0KaHR0cHM6Ly9ib29rZG93bi5vcmcveWlodWkvcm1hcmtkb3duL2Jvb2tkb3duLW1hcmtkb3duLmh0bWwgICMgVGVvcmVtcyBhbmQgcHJvb2ZzDQoNCmh0dHBzOi8vYm9va2Rvd24ub3JnL3lpaHVpL2Jvb2tkb3duL21hcmtkb3duLWV4dGVuc2lvbnMtYnktYm9va2Rvd24uaHRtbCN0aGVvcmVtcw0KDQpodHRwczovL2Jvb2tkb3duLm9yZy95aWh1aS9ib29rZG93bi9odG1sLmh0bWwNCg0KaHR0cHM6Ly93d3cuZGF0YS10by12aXouY29tLw0KICANCltScHVic10obGluaykNCiAgDQooXCNlcTplYy0pLCAgRWN1YWNpb24gXEByZWYoZXE6ZWMtKSwgRmlndXJhIFxAcmVmKGZpZzpGaWctKSwgVGFibGUgXEByZWYodGFiOm10Y2FycyksIFRoZW9yZW0gXEByZWYodGhtOmJvcmluZykNCg0KDQojIFRpdHVsbyB7I1RpdHVsb1NlY2Npb259ICAgXEByZWYoVGl0dWxvU2VjY2lvbikNCiAgDQojIEZvciBIVE1MLCB3ZSBjYW4gc2V0IGNvbG9yIHdpdGggQ1NTLCBlLmcuLCA8c3BhbiBzdHlsZT0iY29sb3I6IHJlZDsiPnRleHQ8L3NwYW4+DQogIA0KIyBodHRwczovL3JhZGlhbnQtcnN0YXRzLmdpdGh1Yi5pby9kb2NzL21vZGVsL2xvZ2lzdGljLmh0bWwgU2hpbm55IExvZ2l0ICANCiAgDQpgYGANCg0KYGBge3IsIGV2YWw9RkFMU0UsIGVjaG89RkFMU0V9DQojTGEgZm90byB0YW1hw7FvIGPDqWR1bGENCg0KaHRtbHRvb2xzOjppbWcoc3JjID0ga25pdHI6OmltYWdlX3VyaShmaWxlLnBhdGgoUi5ob21lKCJkb2MiKSwgImh0bWwiLCAibG9nby5qcGciKSksIA0KICAgICAgICAgICAgICAgYWx0ID0gJ2hsbGluYXMnLCANCiAgICAgICAgICAgICAgIHN0eWxlID0gJ3Bvc2l0aW9uOmFic29sdXRlOyB0b3A6MDsgcmlnaHQ6MDsgcGFkZGluZzoxMHB4OycgIywNCiAgICAgICAgICAgICAgIHdpZHRoID0gIjIwMHB4IikgICMgQXF1w60gZXNwZWNpZmljYXMgZWwgYW5jaG8gZGVzZWFkbyBlbiBww614ZWxlcyBvIHBvcmNlbnRhamUNCmBgYA0KDQoNCg0KDQpgYGB7ciwgZWNobz1GQUxTRSwgfQ0KIyBMYSBmb3RvIGdyYW5kZQ0KDQpodG1sdG9vbHM6OmltZyhzcmMgPSBrbml0cjo6aW1hZ2VfdXJpKCJobGxpbmFzMjAyMy5qcGciKSwgDQogICAgICAgICAgICAgICBhbHQgPSAnaGxsaW5hczIwMjMnLCANCiAgICAgICAgICAgICAgIHN0eWxlID0gJ3Bvc2l0aW9uOmFic29sdXRlOyB0b3A6MDsgcmlnaHQ6MDsgcGFkZGluZzoxcHg7JywNCiAgICAgICAgICAgICAgIHdpZHRoPSIxNSUiKQ0KYGBgDQoNCg0KDQo8IS0tICUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlIC0tPg0KPCEtLSBTZXBhcmFkb3IgIC0tPg0KDQpgYGB7Y3NzLCBlY2hvPUZBTFNFfQ0KLmNvbHVtbnMge2Rpc3BsYXk6IGZsZXg7fQ0KaDEge2NvbG9yOiBkYXJrYmx1ZTt9DQpoMyB7Y29sb3I6IGRhcmtncmVlbjt9DQpoNCB7Y29sb3I6IGdyZWVuO30NCmBgYA0KDQoNCjwhLS0gJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUgLS0+DQo8IS0tIFNlcGFyYWRvciAgLS0+DQoNCiMgTGlicmVyw61hcw0KDQojIyMgUGFyYSAkayQtbWVhbnMNCg0KDQpgYGB7ciwgZXZhbD1GQUxTRSwgZWNobz1GQUxTRX0NCiMgIGh0dHBzOi8vcnB1YnMuY29tL0FsZW1hLzEwMDA1ODINCg0KIyBodHRwczovL3d3dy5nZWVrc2ZvcmdlZWtzLm9yZy9jb250ZXh0dWFsLW91dGxpZXJzLw0KYGBgDQoNCkVsIHNvZnR3YXJlIFIgZGlzcG9uZSBkZSB2YXJpYXMgZnVuY2lvbmVzIGRlIGRpZmVyZW50ZXMgcGFxdWV0ZXMgcGFyYSBsbGV2YXIgYSBjYWJvIHVuIGFuw6FsaXNpcyBkZSBjb25nbG9tZXJhZG9zOg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbGlicmFyeShzdGF0cykNCmxpYnJhcnkoZmFjdG9leHRyYSkNCmxpYnJhcnkoY2x1c3RlcikNCmBgYA0KDQo8IS0tICUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlIC0tPg0KPCEtLSBTZXBhcmFkb3IgIC0tPg0KDQojIyMgUGFyYSBvdHJvcyBhbsOhbGlzaXMNCg0KYGBge3IsICBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBlY2hvPUZBTFNFfQ0KbGlicmFyeShhcGxvcmUzKSAgICAjQmFzZSBkZSBkYXRvcyBwYXJhIGxvcyBlamVtcGxvcw0KbGlicmFyeShsc20pICAgICAgICAjQmFzZSBkZSBkYXRvcyBwYXJhIGVqZW1wbG9zIHkgZXN0aW1hY2lvbmVzIGRlbCBMb2ctdmVyb3NpbWlsaXR1ZA0KbGlicmFyeSh0aWR5dmVyc2UpICAjSW5jbHV5ZSBhIGRwbHlyIHkgZ2dwbG90Mg0KbGlicmFyeShzdHJpbmdyKSAgICAjUmVlbXBsYXphciBjYXJhY3RlcmVzIGVuIHVuIGRhdGEgZnJhbWUNCmxpYnJhcnkob3V0bGllcnMpICAgI291dGxpZXJzOjpncnViYnMudGVzdA0KbGlicmFyeShFbnZTdGF0cykgICAjRW52U3RhdHM6OnJvc25lclRlc3QNCmxpYnJhcnkoRE13UjIpICAgICAgI0xPRiAoTG9jYWwgT3V0bGllciBGYWN0b3IpDQpsaWJyYXJ5KHJnbCkgICAgICAgICNyZ2w6OnBsb3QzZA0KbGlicmFyeShjb3JycGxvdCkgICAjTWF0cml6IGRlIGNvcnJlbGFjaW9uZXMNCmxpYnJhcnkodGV4dHNoYXBlKSAgI2NvbHVtbl90b19yb3duYW1lcw0KbGlicmFyeShvcGVueGxzeCkgICAjTGlicmVyw61hIHBhcmEgZXNjcmliaXIgYXJjaGl2b3MgZGUgRXhjZWwNCmxpYnJhcnkobXZ0bm9ybSkgICAgI0dlbmVyYXIgdW5hIGRpc3RyaWJ1Y2nDs24gYWxlYXRvcmlhIG5vcm1hbCBtdWx0aXZhcmlhbnRlDQpsaWJyYXJ5KGNhcmV0KSAgICAgICNjYXJldDo6Y29uZnVzaW9uTWF0cml4DQpsaWJyYXJ5KGtuaXRyKSAgICAgICNjcmVhciB0YWJsYXMgY29uIGVzdGlsbw0KbGlicmFyeShrYWJsZUV4dHJhKSAjY3JlYXIgdGFibGFzIGNvbiBlc3RpbG8sIHBlcm8gcGFyYSBodG1sDQojb3B0c19rbml0JHNldChldmFsLmFmdGVyID0gJ2ZpZy5jYXAnKQ0KYGBgDQoNCmBgYHtjc3MsIGVjaG89RkFMU0UsIGV2YWw9RkFMU0V9DQojaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvNDEwMzA0NzcvY2hhbmdpbmctY2h1bmstYmFja2dyb3VuZC1jb2xvci1pbi1ybWFya2Rvd24NCg0KLmJhZENvZGUgew0KYmFja2dyb3VuZC1jb2xvcjogcmVkOw0KfQ0KYGBgDQoNCmBgYHtyLCAgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZXZhbD1GQUxTRX0NCmxpYnJhcnkoYXBsb3JlMykgICAgI0Jhc2UgZGUgZGF0b3MgcGFyYSBsb3MgZWplbXBsb3MNCmxpYnJhcnkobHNtKSAgICAgICAgI0Jhc2UgZGUgZGF0b3MgcGFyYSBlamVtcGxvcyB5IGVzdGltYWNpb25lcyBkZWwgTG9nLXZlcm9zaW1pbGl0dWQNCmxpYnJhcnkodGlkeXZlcnNlKSAgI0luY2x1eWUgYSBkcGx5ciB5IGdncGxvdDINCmxpYnJhcnkoc3RyaW5ncikgICAgI1JlZW1wbGF6YXIgY2FyYWN0ZXJlcyBlbiB1biBkYXRhIGZyYW1lDQpsaWJyYXJ5KG91dGxpZXJzKSAgICNvdXRsaWVyczo6Z3J1YmJzLnRlc3QNCmxpYnJhcnkoRW52U3RhdHMpICAgI0VudlN0YXRzOjpyb3NuZXJUZXN0DQpsaWJyYXJ5KERNd1IyKSAgICAgICNMT0YgKExvY2FsIE91dGxpZXIgRmFjdG9yKQ0KbGlicmFyeShyZ2wpICAgICAgICAjcmdsOjpwbG90M2QNCmxpYnJhcnkoY29ycnBsb3QpICAgI01hdHJpeiBkZSBjb3JyZWxhY2lvbmVzDQpsaWJyYXJ5KHRleHRzaGFwZSkgICNjb2x1bW5fdG9fcm93bmFtZXMNCmxpYnJhcnkob3Blbnhsc3gpICAgI0xpYnJlcsOtYSBwYXJhIGVzY3JpYmlyIGFyY2hpdm9zIGRlIEV4Y2VsDQpsaWJyYXJ5KG12dG5vcm0pICAgICNHZW5lcmFyIHVuYSBkaXN0cmlidWNpw7NuIGFsZWF0b3JpYSBub3JtYWwgbXVsdGl2YXJpYW50ZQ0KbGlicmFyeShjYXJldCkgICAgICAjY2FyZXQ6OmNvbmZ1c2lvbk1hdHJpeA0KbGlicmFyeShrbml0cikgICAgICAjY3JlYXIgdGFibGFzIGNvbiBlc3RpbG8NCmxpYnJhcnkoa2FibGVFeHRyYSkgI2NyZWFyIHRhYmxhcyBjb24gZXN0aWxvLCBwZXJvIHBhcmEgaHRtbA0KYGBgDQoNCg0KPCEtLSAlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlIC0tPg0KPCEtLSBTZXBhcmFkb3IgLS0+DQoNCiMgSW50cm9kdWNjacOzbg0KDQoxLiBFbCBhZ3J1cGFtaWVudG8gJGskLW1lYW5zLCBwcm9wdWVzdG8gcG9yIE1hY1F1ZWVuIGVuIDE5NjcsIGVzIGVsIGFsZ29yaXRtbyBkZSBhcHJlbmRpemFqZSBhdXRvbcOhdGljbyBubyBzdXBlcnZpc2FkbyBtw6FzIHV0aWxpemFkbyBwYXJhIGRpdmlkaXIgdW4gY29uanVudG8gZGUgZGF0b3MgZW4gdW4gbsO6bWVybyBwcmVkZWZpbmlkbyBkZSBncnVwb3MgKCRrJCBjbHVzdGVycykuIA0KDQoyLiBFc3RlIG3DqXRvZG8gY2xhc2lmaWNhIGxvcyBvYmpldG9zIGVuIHZhcmlvcyBncnVwb3MsIGFzZWd1cmFuZG8gcXVlIGxvcyBvYmpldG9zIGRlbnRybyBkZWwgbWlzbW8gZ3J1cG8gc2VhbiBsbyBtw6FzIHNpbWlsYXJlcyBwb3NpYmxlLCBtaWVudHJhcyBxdWUgbG9zIG9iamV0b3MgZGUgZGlmZXJlbnRlcyBncnVwb3Mgc2VhbiBsbyBtw6FzIGRpZmVyZW50ZXMgcG9zaWJsZS4gDQoNCjMuIENhZGEgZ3J1cG8gZW4gZWwgY2x1c3RlcmluZyAkayQtbWVhbnMgc2UgY2FyYWN0ZXJpemEgcG9yIHN1IGNlbnRyb2lkZSwgcXVlIGVzIGxhIG1lZGlhIGRlIGxvcyBwdW50b3MgYXNpZ25hZG9zIGFsIGdydXBvLg0KDQo0LiBWw6lhc2UgbGFzIEZpZ3VyYXMgXEByZWYoZmlnOmttZWFuMSkgeSBcQHJlZihmaWc6a21lYW4yKS4gDQoNCjxjZW50ZXI+DQoNCmBgYHtyIGttZWFuMSwgZWNobz1GQUxTRSwgZmlnLmNhcCA9ICIqKiRrJC1tZWFucyoqIiwgb3V0LndpZHRoID0gIjcwJSJ9DQojIGZpZy53aWR0aCA9IDIwICMgTm8gZnVuY2lvbmEgZXN0YSBvcGNpb24gZW4gZWwgY2h1bmsNCg0KI2h0dHA6Ly96ZXZyb3NzLmNvbS9ibG9nLzIwMTcvMDYvMTkvdGlwcy1hbmQtdHJpY2tzLWZvci13b3JraW5nLXdpdGgtaW1hZ2VzLWFuZC1maWd1cmVzLWluLXItbWFya2Rvd24tZG9jdW1lbnRzLw0KIyBQYWdpbmEgMzU5IGRlIFIyMDE1LUZyaWVuZGx5DQoNCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJrbWVhbjEucG5nIikNCg0KI090cmEgbWFuZXJhLCBwZXJvICBzYWxlIGVsIGNhcHRpb246DQojPGNlbnRlcj4NCiMhWygjZmlnOkZpZy1jYXB0aW9uKSBNaSBmaWd1cmFdKE5vbWJyZS5wbmcpe3dpZHRoPTQwMHB4fQ0KIzwvY2VudGVyPg0KYGBgDQo8L2NlbnRlcj4NCg0KPGNlbnRlcj4NCg0KYGBge3Iga21lYW4yLCBlY2hvPUZBTFNFLCBmaWcuY2FwID0gIioqRWplbXBsbyBwcsOhY3RpY28gY29uICRrJC1tZWFucyoqIiwgb3V0LndpZHRoID0gIjYwJSJ9DQojIGZpZy53aWR0aCA9IDIwICMgTm8gZnVuY2lvbmEgZXN0YSBvcGNpb24gZW4gZWwgY2h1bmsNCg0KI2h0dHA6Ly96ZXZyb3NzLmNvbS9ibG9nLzIwMTcvMDYvMTkvdGlwcy1hbmQtdHJpY2tzLWZvci13b3JraW5nLXdpdGgtaW1hZ2VzLWFuZC1maWd1cmVzLWluLXItbWFya2Rvd24tZG9jdW1lbnRzLw0KIyBQYWdpbmEgMzU5IGRlIFIyMDE1LUZyaWVuZGx5DQoNCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJrbWVhbjIucG5nIikNCg0KI090cmEgbWFuZXJhLCBwZXJvICBzYWxlIGVsIGNhcHRpb246DQojPGNlbnRlcj4NCiMhWygjZmlnOkZpZy1jYXB0aW9uKSBNaSBmaWd1cmFdKE5vbWJyZS5wbmcpe3dpZHRoPTQwMHB4fQ0KIzwvY2VudGVyPg0KYGBgDQo8L2NlbnRlcj4NCg0KDQo8IS0tICUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUgLS0+DQo8IS0tIFNlcGFyYWRvciAtLT4NCg0KIyBJZGVhIGLDoXNpY2EgZGVsICRrJC1tZWFucw0KDQoxLiBMYSBlc2VuY2lhIGZ1bmRhbWVudGFsIGRlbCBjbHVzdGVyaW5nICRrJC1tZWFucyByYWRpY2EgZW4gbGEgZGVmaW5pY2nDs24gZGUgZ3J1cG9zIGRlIHRhbCBtYW5lcmEgcXVlIHNlIG1pbmltaWNlIGxhIHZhcmlhY2nDs24gdG90YWwgZGVudHJvIGRlIGNhZGEgZ3J1cG8gKGNvbm9jaWRhIGNvbW8gdmFyaWFjacOzbiBpbnRyYS1jbHVzdGVyIHRvdGFsKS4gDQoNCjIuIEhheSBkaXZlcnNvcyBhbGdvcml0bW9zIGRlICRrJC1tZWFucyBkaXNwb25pYmxlcywgc2llbmRvIGVsICoqYWxnb3JpdG1vIGVzdMOhbmRhciBlbCBkZSBIYXJ0aWdhbi1Xb25nICgxOTc5KSoqLCBlbCBjdWFsIGRlZmluZSBsYSB2YXJpYWNpw7NuIHRvdGFsIGRlbnRybyBkZWwgY2x1c3RlciBjb21vIGxhIHN1bWEgZGUgbGFzIGRpc3RhbmNpYXMgZXVjbGlkaWFuYXMgYWwgY3VhZHJhZG8gZW50cmUgbG9zIGVsZW1lbnRvcyB5IHN1cyBjZW50cm9pZGVzIHJlc3BlY3Rpdm9zLiANCg0KMy4gU3Ugb2JqZXRpdm8gZXMgYWdydXBhciAkbiQgcHVudG9zIGVuICRrJCBjbMO6c3RlcmVzIGRlIG1hbmVyYSBxdWUgKmxhIHN1bWEgZGUgbGFzIGRpc3RhbmNpYXMgYWwgY3VhZHJhZG8qIGRlIGNhZGEgcHVudG8gYSBzdSBjbMO6c3RlciBjZW50cm9pZGUgY29ycmVzcG9uZGllbnRlIHNlYSBtaW5pbWl6YWRhLg0KDQo0LiBMb3MgcGFzb3MgZGVsIGFsZ29yaXRtbyBzZSB2YW4gYSBleHBsaWNhciBlbiBsYSBzZWNjacOzbiBzaWd1aWVudGUuDQoNCg0KPCEtLSAlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlIC0tPg0KPCEtLSBTZXBhcmFkb3IgLS0+DQoNCiMgQWxnb3JpdG1vIGRlIEhhcnRpZ2FuLVdvbmcNCg0KDQojIyMgJGskLW1lYW5zOiBwYXNvcyBkZWwgYWxnb3JpdG1vIA0KDQpFbCBhbGdvcml0bW8gJGskLW1lYW5zIHB1ZWRlIHJlc3VtaXJzZSBhc8OtOg0KDQoqKjEuIEluaWNpYWxpemFjacOzbi4qKg0KICANCiAgKyBFc3BlY2lmaWNhciBsYSBjYW50aWRhZCBkZSBncnVwb3MgKCRrJCkgcXVlIHNlIGNyZWFyw6FuIChkZXRlcm1pbmFkbyBwb3IgZWwgYW5hbGlzdGEpLg0KICANCiAgKyBTZWxlY2Npb25hciBhbGVhdG9yaWFtZW50ZSAkayQgb2JqZXRvcyBkZWwgY29uanVudG8gZGUgZGF0b3MgY29tbyBsb3MgY2VudHJvaWRlcyBpbmljaWFsZXMgKG1lZGlhcykgZGUgbG9zIGdydXBvcy4NCiAgDQogICsgRWwgY2VudHJvaWRlIGRlIHVuIGdydXBvIGVzIHVuIHZlY3RvciBkZSBsb25naXR1ZCAkcCQgcXVlIGNvbnRpZW5lIGxhcyBtZWRpYXMgZGUgdG9kYXMgbGFzIHZhcmlhYmxlcyBwYXJhIGxhcyBvYnNlcnZhY2lvbmVzIGVuIGVzZSBncnVwbywgIGRvbmRlICRwJCBlcyBlbCBuw7ptZXJvIGRlIHZhcmlhYmxlcy4NCiAgDQogIA0KICANCioqMi4gQXNpZ25hY2nDs24gZGUgQ2zDunN0ZXJlcy4qKg0KICANCiAgKyBBc2lnbmFyIGNhZGEgb2JzZXJ2YWNpw7NuIGEgc3UgY2VudHJvaWRlIG3DoXMgY2VyY2FubywgYmFzw6FuZG9zZSBlbiBsYSBkaXN0YW5jaWEgZXVjbGlkaWFuYSBlbnRyZSBlbCBvYmpldG8geSBlbCBjZW50cm9pZGUuDQogIA0KICArIEVzdGEgZXRhcGEgc2UgY29ub2NlIGNvbW8gKnBhc28gZGUgYXNpZ25hY2nDs24gZGUgZ3J1cG9zKi4NCiAgDQogICsgRXMgaW1wb3J0YW50ZSBkZXN0YWNhciBxdWUsIHBhcmEgZW1wbGVhciBsYSBkaXN0YW5jaWEgZGUgY29ycmVsYWNpw7NuLCBsb3MgZGF0b3Mgc2UgaW5ncmVzYW4gY29tbyBwdW50dWFjaW9uZXMgJFokLg0KDQoNCioqMy4gQWN0dWFsaXphY2nDs24gZGUgQ2VudHJvaWRlcyoqLg0KICANCiAgKyBQYXJhIGNhZGEgdW5vIGRlIGxvcyAkayQgZ3J1cG9zLCBhY3R1YWxpemFyIGVsIGNlbnRyb2lkZSBkZWwgZ3J1cG8gY2FsY3VsYW5kbyBsb3MgbnVldm9zIHZhbG9yZXMgbWVkaW9zIGRlIHRvZG9zIGxvcyBwdW50b3MgZGUgZGF0b3MgZW4gZWwgZ3J1cG8uIA0KICANCiAgKyBTZSB1dGlsaXphIGVsIHTDqXJtaW5vICphY3R1YWxpemFjacOzbiBkZWwgY2VudHJvaWRlIGRlbCBncnVwbyogcGFyYSBkZXNjcmliaXIgZXN0YSBmYXNlLg0KICANCiAgKyBBaG9yYSBxdWUgbG9zIGNlbnRyb2lkZXMgaGFuIHNpZG8gcmVjYWxjdWxhZG9zLCBjYWRhIG9ic2VydmFjacOzbiBzZSByZXZpc2EgbnVldmFtZW50ZSBwYXJhIGRldGVybWluYXIgc2kgcG9kcsOtYSBlc3RhciBtw6FzIGNlcmNhbmEgYSBvdHJvIGdydXBvLiBUb2RvcyBsb3Mgb2JqZXRvcyBzZSByZWFzaWduYW4gdXRpbGl6YW5kbyBsb3MgY2VudHJvaWRlcyBkZSBncnVwbyBhY3R1YWxpemFkb3MuDQogDQoqKjQuIEl0ZXJhY2nDs24uKiogDQogIA0KICArIFJlcGV0aXIgbG9zIHBhc29zIGRlIGFzaWduYWNpw7NuIHkgYWN0dWFsaXphY2nDs24gaGFzdGEgcXVlIGxhIGFzaWduYWNpw7NuIGRlIHB1bnRvcyBhIGxvcyBjbMO6c3RlcmVzIG5vIGNhbWJpZSBzaWduaWZpY2F0aXZhbWVudGUgZW50cmUgaXRlcmFjaW9uZXMgbyBoYXN0YSBxdWUgc2UgYWxjYW5jZSB1biBuw7ptZXJvIG3DoXhpbW8gZGUgaXRlcmFjaW9uZXMuDQogIA0KICArIExhIGlkZWEgZXMgbWluaW1pemFyIGRlIG1hbmVyYSBpdGVyYXRpdmEgbGEgc3VtYSB0b3RhbCBkZSBjdWFkcmFkb3MgZGVudHJvIGRlIGxvcyBncnVwb3MsIGVzIGRlY2lyLCBpdGVyYXIgbG9zIHBhc29zIDMgeSA0IGhhc3RhIHF1ZSBsYXMgYXNpZ25hY2lvbmVzIGRlIGdydXBvIGRlamVuIGRlIGNhbWJpYXIgbyBzZSBhbGNhbmNlIGVsIG7Dum1lcm8gbcOheGltbyBkZSBpdGVyYWNpb25lcy4gDQogIA0KICArIFBvciBkZWZlY3RvLCBlbCBzb2Z0d2FyZSBgUmAgdXRpbGl6YSAxMCBjb21vIHZhbG9yIHByZWRldGVybWluYWRvIHBhcmEgZWwgbsO6bWVybyBtw6F4aW1vIGRlIGl0ZXJhY2lvbmVzLg0KICANCiAgDQoqKjUuIFJlZ2xhIGRlIEhhcnRpZ2FuLioqDQogIA0KICArIEVuIGNhZGEgaXRlcmFjacOzbiwgZXZhbHVhciBzaSBtb3ZlciB1biBwdW50byBkZSBzdSBjbMO6c3RlciBhY3R1YWwgYSBvdHJvIGNsw7pzdGVyIHJlZHVjZSBsYSBzdW1hIHRvdGFsIGRlIGN1YWRyYWRvcyBpbnRyYS1jbMO6c3Rlci4gU2kgZXMgYXPDrSwgaGFjZXIgZWwgbW92aW1pZW50byB5IGFjdHVhbGl6YXIgbG9zIGNlbnRyb2lkZXMuDQogIA0KDQoqKjYuIENvdmVyZ2VuY2lhLioqDQoNCiAgICsgTG9zIHBhc29zIGRlIGFzaWduYWNpw7NuIGRlIGdydXBvcyB5IGFjdHVhbGl6YWNpw7NuIGRlIGNlbnRyb2lkZXMgc2UgcmVwaXRlbiBkZSBtYW5lcmEgaXRlcmF0aXZhIGhhc3RhIHF1ZSBsYXMgYXNpZ25hY2lvbmVzIGRlIGdydXBvIGRlamVuIGRlIGNhbWJpYXIsIGVzIGRlY2lyLCBoYXN0YSBxdWUgc2UgbG9ncmUgbGEgY29udmVyZ2VuY2lhLg0KDQogKyBFbCBhbGdvcml0bW8gY29udmVyZ2UgY3VhbmRvIHlhIG5vIHNlIHByb2R1Y2VuIGNhbWJpb3Mgc2lnbmlmaWNhdGl2b3MgZW4gbGEgYXNpZ25hY2nDs24gZGUgcHVudG9zIGEgbG9zIGNsw7pzdGVyZXMgbyBsYSBzdW1hIGRlIGN1YWRyYWRvcyBpbnRyYS1jbMO6c3RlciBubyBwdWVkZSByZWR1Y2lyc2UgbcOhcy4NCiANCiArIEVzdG8gaW1wbGljYSBxdWUgbG9zIGdydXBvcyBmb3JtYWRvcyBlbiBsYSBpdGVyYWNpw7NuIGFjdHVhbCBzb24gbG9zIG1pc21vcyBxdWUgbG9zIG9idGVuaWRvcyBlbiBsYSBpdGVyYWNpw7NuIGFudGVyaW9yLg0KDQo8IS0tICUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUgLS0+DQo8IS0tIFNlcGFyYWRvciAtLT4NCg0KDQojIyMgJGskLW1lYW5zOiBzdW1hcyBkZSBjdWFkcmFkb3MNCg0KKioxLiAkXHRleHR7V2l0aGluc3N9X2skIChTdW1hIGRlIGN1YWRyYWRvcyBkZW50cm8gZGVsIGNsw7pzdGVyICRrJCkuKioNCg0KRWwgbGxhbWFkbyAqdmVjdG9yIGRlIHN1bWEgZGUgY3VhZHJhZG9zIGRlbnRybyBkZWwgY2zDunN0ZXIgJGskKiAgZXMgZGVmaW5pZG8gcG9yOiANCg0KJCRcdGV4dHtXaXRoaW5zc31fayBcLD1cOyBXKENfaykgXDs9XDsgXHN1bVxsaW1pdHNfe3hfaSBcaW4gQ19rfSAoeF9pIC1cb3ZlcmxpbmV7eH1fayleMlw7PVw7IFxzdW1cbGltaXRzX3t4X2kgXGluIENfa30gZF9pXjIkJA0KDQoqKjIuICRcdGV4dHtXaXRoaW5zc31fayQgKG5vdGFjaW9uZXMpKiouDQoNCkVuIGxhIGbDs3JtdWxhIGFudGVyaW9yOiANCg0KICAgKyAkeF9pJCByZXByZXNlbnRhIHVuIHB1bnRvIGRlIGRhdG9zIHF1ZSBwZXJ0ZW5lY2UgYWwgZ3J1cG8gJENfayQuDQogICAgDQogICArICRcb3ZlcmxpbmV7eH1fayQgcmVwcmVzZW50YSBsYSBtZWRpYSBkZSBsb3MgcHVudG9zIGFzaWduYWRvcyBhbCBncnVwbyAkQ19rJC4NCiAgIA0KICAgKyAkZF9pID0geF9pIC0gXG92ZXJsaW5le3h9X2skIGxhIGRpc3RhbmNpYSBkZWwgcHVudG8gJHhfaSQgYWwgY2VudHJvaWRlICRcb3ZlcmxpbmV7eH1fayQsIGRlbnRybyBkZWwgY2zDunN0ZXIgJENfayQuDQogICANCg0KKiozLiAkXHRleHR7V2l0aGluc3N9X2skIChncsOhZmljbykuKioNCg0KVsOpYXNlIGxhIGZpZ3VyYSBcQHJlZihmaWc6a21lYW4zKS4gDQoNCjxjZW50ZXI+DQoNCmBgYHtyIGttZWFuMywgZWNobz1GQUxTRSwgZmlnLmNhcCA9ICIqKldpdGhpbnNzJF9rJCAodmVjdG9yIGRlIHN1bWEgZGUgY3VhZHJhZG9zIGRlbnRybyBkZWwgY2zDunN0ZXIgJGskKSoqIiwgb3V0LndpZHRoID0gIjEwMCUifQ0KIyBmaWcud2lkdGggPSAyMCAjIE5vIGZ1bmNpb25hIGVzdGEgb3BjaW9uIGVuIGVsIGNodW5rDQoNCiNodHRwOi8vemV2cm9zcy5jb20vYmxvZy8yMDE3LzA2LzE5L3RpcHMtYW5kLXRyaWNrcy1mb3Itd29ya2luZy13aXRoLWltYWdlcy1hbmQtZmlndXJlcy1pbi1yLW1hcmtkb3duLWRvY3VtZW50cy8NCiMgUGFnaW5hIDM1OSBkZSBSMjAxNS1GcmllbmRseQ0KDQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygia21lYW4zLnBuZyIpDQoNCiNPdHJhIG1hbmVyYSwgcGVybyAgc2FsZSBlbCBjYXB0aW9uOg0KIzxjZW50ZXI+DQojIVsoI2ZpZzpGaWctY2FwdGlvbikgTWkgZmlndXJhXShOb21icmUucG5nKXt3aWR0aD00MDBweH0NCiM8L2NlbnRlcj4NCmBgYA0KPC9jZW50ZXI+DQoNCg0KKio0LiAkXHRleHR7V2l0aGluc3N9X2skIChhc2lnbmFjacOzbikuKioNCiAgICANCkNhZGEgb2JzZXJ2YWNpw7NuICgkeF9pJCkgc2UgYXNpZ25hIGEgdW4gY2zDunN0ZXIgZXNwZWPDrWZpY28gZGUgbW9kbyBxdWUgbGEgc3VtYSBkZSBsb3MgY3VhZHJhZG9zIChTUykgZGUgbGFzIGRpc3RhbmNpYXMgZW50cmUgbGEgb2JzZXJ2YWNpw7NuIHkgbG9zIGNlbnRyb3MgZGUgY2zDunN0ZXIgYXNpZ25hZG9zICRcb3ZlcmxpbmV7eH1fayQgc2VhIGxhIG1lbm9yIHBvc2libGUuIA0KDQoNCioqNS4gJFx0ZXh0e1RvdC53aXRoaW5zc30kICh2YXJpYWJpbGlkYWQgdG90YWwgZGVudHJvIGRlIHRvZG9zIGxvcyBjbMO6c3RlcikuKioNCiAgDQogICsgTGEgKnN1bWEgdG90YWwgZGUgY3VhZHJhZG9zIGRlbnRybyBkZSB0b2RvcyBsb3MgY2zDunN0ZXJzKiBlcyB1bmEgbWVkaWRhIGRlIHF1w6kgdGFuIGNvbXBhY3RvIGVzIGVsIGFncnVwYW1pZW50byAoZXMgZGVjaXIsIHN1IGNhbGlkYWQpLiANCiAgDQogICsgUmVwcmVzZW50YSBsYSB2YXJpYWJpbGlkYWQgdG90YWwgZGVudHJvIGRlIHRvZG9zIGxvcyBjbHVzdGVycy4gDQogIA0KICArIFVuIHZhbG9yIG1heW9yIGRlICRcdGV4dHtUb3Qud2l0aGluc3N9JCBpbmRpY2EgcXVlIGxvcyBwdW50b3MgZGVudHJvIGRlIGNhZGEgY2x1c3RlciBlc3TDoW4gbcOhcyBkaXNwZXJzb3MsIGxvIHF1ZSBzdWdpZXJlIHVuYSBtZW5vciBjYWxpZGFkIGRlIGNsdXN0ZXJpbmcuDQogIA0KICArIFNlIGRlZmluZSBkZSBsYSBzaWd1aWVudGUgbWFuZXJhOg0KDQokJFx0ZXh0e1RvdC53aXRoaW5zc30gXCw9XDtcc3VtXGxpbWl0c197az0xfV5LIFcoQ19rKSBcOz1cOyBcc3VtXGxpbWl0c197az0xfV5LXHN1bVxsaW1pdHNfe3hfaSBcaW4gQ19rfSAoeF9pIC1cb3ZlcmxpbmV7eH1fayleMlw7PVw7IFxzdW1cbGltaXRzX3trPTF9Xktcc3VtXGxpbWl0c197eF9pIFxpbiBDX2t9IGRfaV4yJCQNCg0KKio2LiAkXHRleHR7VG90LndpdGhpbnNzfSQgKGdyw6FmaWNvKS4qKg0KDQpWw6lhc2UgbGEgZmlndXJhIFxAcmVmKGZpZzprbWVhbjQpLiANCg0KPGNlbnRlcj4NCg0KYGBge3Iga21lYW40LCBlY2hvPUZBTFNFLCBmaWcuY2FwID0gIioqVG90LndpdGhpbnNzICh2YXJpYWJpbGlkYWQgdG90YWwgZGVudHJvIGRlIHRvZG9zIGxvcyBjbMO6c3RlcikqKiIsIG91dC53aWR0aCA9ICIxMDAlIn0NCiMgZmlnLndpZHRoID0gMjAgIyBObyBmdW5jaW9uYSBlc3RhIG9wY2lvbiBlbiBlbCBjaHVuaw0KDQojaHR0cDovL3pldnJvc3MuY29tL2Jsb2cvMjAxNy8wNi8xOS90aXBzLWFuZC10cmlja3MtZm9yLXdvcmtpbmctd2l0aC1pbWFnZXMtYW5kLWZpZ3VyZXMtaW4tci1tYXJrZG93bi1kb2N1bWVudHMvDQojIFBhZ2luYSAzNTkgZGUgUjIwMTUtRnJpZW5kbHkNCg0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoImttZWFuNC5wbmciKQ0KDQojT3RyYSBtYW5lcmEsIHBlcm8gIHNhbGUgZWwgY2FwdGlvbjoNCiM8Y2VudGVyPg0KIyFbKCNmaWc6RmlnLWNhcHRpb24pIE1pIGZpZ3VyYV0oTm9tYnJlLnBuZyl7d2lkdGg9NDAwcHh9DQojPC9jZW50ZXI+DQpgYGANCjwvY2VudGVyPg0KDQoqKioqDQoNCioqOC4gT2JqZXRpdm8gZmluYWwuKioNCg0KTnVlc3RybyBvYmpldGl2byBlcyBtaW5pbWl6YXIgZXN0YSBzdW1hIHRhbnRvIGNvbW8gc2VhIHBvc2libGU6DQoNCiQkXG1pbihcdGV4dHtUb3Qud2l0aGluc3N9KSAgXCw9XDtcbWluIFxzdW1cbGltaXRzX3trPTF9XksgVyhDX2spJCQNCg0KPCEtLSAlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlIC0tPg0KPCEtLSBTZXBhcmFkb3IgLS0+DQoNCiMjIyAkayQtbWVhbnM6IHByb3BvcmNpw7NuIGRlIHZhcmlhbnphIGV4cGxpY2FkYQ0KDQoqKjEuICRcdGV4dHtUb3Rzc30kICh2YXJpYWJpbGlkYWQgdG90YWwgZW4gdG9kb3MgbG9zIGRhdG9zKS4qKg0KDQpMYSBzdW1hIHRvdGFsIGRlIGN1YWRyYWRvcyBtaWRlIGxhIHZhcmlhYmlsaWRhZCB0b3RhbCBlbiBsb3MgZGF0b3MgICB5IG1pZGUgbGEgdmFyaWFiaWxpZGFkIHRvdGFsIGVuIHRvZG9zIGxvcyBkYXRvcyAoc2luIHBhcnRpY2lvbmFyIGxvcyBkYXRvcyBvcmlnaW5hbGVzLCBjb21vIHNpIHNvbG8gdHV2acOpc2Vtb3MgdW4gc29sbyBjbMO6c3RlcikuDQoNCg0KKioyLiAkXHRleHR7VG90c3N9JCAoZGVmaW5pY2nDs24pLioqDQoNCkxhIHN1bWEgdG90YWwgZGUgY3VhZHJhZG9zIHNlIGRlZmluZSBjb21vOiANCg0KJCRcdGV4dHtUb3Rzc30gXDsgPVw7IFxzdW1cbGltaXRzX3tpPTF9Xm4gKHhfaSAtIFxvdmVybGluZXt4fSleMiQkDQoNCioqMy4gJFx0ZXh0e0JldHdlZW5zc30kIChzdW1hIGRlIGN1YWRyYWRvcyBlbnRyZSBjbMO6c3RlcmVzKS4qKg0KDQpMYSBzdW1hIGRlIGN1YWRyYWRvcyBlbnRyZSBjbMO6c3RlcmVzLCBxdWUgbWlkZSBsYSB2YXJpYWNpw7NuIGRlYmlkbyBhIGxhcyBkaWZlcmVuY2lhcyBlbnRyZSBsb3MgY2VudHJvaWRlcyBkZSBsb3MgY2zDunN0ZXJlcywgc2UgZGVmaW5lIGNvbW86IA0KDQokJFx0ZXh0e0JldHdlZW5zc30gXDsgPSBcOyBcdGV4dHtUb3Rzc30gXCwtXCwgIFx0ZXh0e1RvdC53aXRoaW5zc30kJA0KDQoqKjQuICRcdGV4dHtQcm9wLlZhcn0kIChwcm9wb3JjacOzbiBkZSB2YXJpYW56YSB0b3RhbCkuKioNCg0KTGEgcHJvcG9yY2nDs24gZGUgdmFyaWFuemEgdG90YWwgZW4gbG9zIGRhdG9zIHF1ZSBlcyBleHBsaWNhZGEgcG9yIGxhIHZhcmlhY2nDs24gZW50cmUgbG9zIGNlbnRyb2lkZXMgZGUgbG9zIGNsw7pzdGVyZXMgKHF1ZSBlcyBleHBsaWNhZGEgcG9yIGxhIGFncnVwYWNpw7NuIGRlIGxvcyBkYXRvcyBlbiBsb3MgY2zDunN0ZXJlcykgc2UgY2FsY3VsYSBhc8OtOg0KDQokJFx0ZXh0e1Byb3AuVmFyfVw7ID0gXDtcZnJhY3tcdGV4dHtCZXR3ZWVuc3N9ICB9e1x0ZXh0e1RvdHNzfX0gXDsgPSBcOyAxXDstXDsgXGZyYWN7XHRleHR7VG90LndpdGhpbnNzfX17XHRleHR7VG90c3N9fSQkDQoNCioqNS4gJFx0ZXh0e1Byb3AuVmFyfSQgKGludGVycHJldGFjacOzbikuKioNCg0KTGEgaW50ZXJwcmV0YWNpw7NuIGRlIGxhIHByb3BvcmNpw7NuIGRlIHZhcmlhbnphIGV4cGxpY2FkYSBlcyBjb21vIHNpZ3VlOiANCiAgDQogICsgVW4gdmFsb3IgbcOhcyBhbHRvIChjZXJjYW5vIGEgMTAwJSkgaW5kaWNhIHF1ZSBsb3MgY2zDunN0ZXJlcyBmb3JtYWRvcyBleHBsaWNhbiBiaWVuIGxhIHZhcmlhY2nDs24gdG90YWwgZW4gbG9zIGRhdG9zLCBzdWdpcmllbmRvIHF1ZSBsb3MgY2zDunN0ZXJlcyBlc3TDoW4gYmllbiBkZWZpbmlkb3MgeSBzZXBhcmFkb3MuDQogIA0KICArIFVuIHZhbG9yIG3DoXMgYmFqbyBzdWdpZXJlIHF1ZSBsb3MgY2zDunN0ZXJlcyBubyBleHBsaWNhbiBiaWVuIGxhIHZhcmlhY2nDs24gZW4gbG9zIGRhdG9zLCBsbyBxdWUgcG9kcsOtYSBpbmRpY2FyIHF1ZSBsb3MgY2zDunN0ZXJlcyBubyBlc3TDoW4gYmllbiBkZWZpbmlkb3MgbyBxdWUgbm8gaGF5IHVuYSBlc3RydWN0dXJhIGNsYXJhIGVuIGxvcyBkYXRvcy4NCiAgDQoNCjwhLS0gJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSAtLT4NCjwhLS0gU2VwYXJhZG9yIC0tPg0KDQojIEVqZW1wbG86IGRhdG9zDQoNCiMjIyBCYXNlIGRlIGRhdG9zDQoNCkxvcyBkYXRvcyBzZSByZWNvZ2llcm9uIGFwbGljYW5kbyB1bmEgZW5jdWVzdGEgYSB1bmEgbXVlc3RyYSBkZSBlc3R1ZGlhbnRlcyB1bml2ZXJzaXRhcmlvcy4gRXMgdW4gZGF0YSBmcmFtZSBjb24gODAwIG9ic2VydmFjaW9uZXMgeSA2NiB2YXJpYWJsZXMuIENvbiBlc3RvcyBkYXRvcyBsbGV2YXJlbW9zIGEgY2FibyB1biBQQ0EuIA0KDQoNCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KZGF0b3NDb21wbGV0byA8LSBsc206OnN1cnZleQ0KI2RhdG9zQ29tcGxldG8gPC0gdGV4dHNoYXBlOjpjb2x1bW5fdG9fcm93bmFtZXMoZGF0LCBsb2M9MSkNCiNkYXRvc0NvbXBsZXRvICU+JSByZW1vdmVfcm93bmFtZXMgJT4lIGNvbHVtbl90b19yb3duYW1lcyh2YXI9Im5hbWVzIikgICAjbGlicmFyeSh0aWR5dmVyc2UpDQphdHRhY2goZGF0b3NDb21wbGV0bykNCmBgYA0KDQoNCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbmFtZXMoZGF0b3NDb21wbGV0bykNCmBgYA0KDQoNCjwhLS0gJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSAtLT4NCjwhLS0gU2VwYXJhZG9yIC0tPg0KDQojIyMgU29sbyBkYXRvcyBudW3DqXJpY29zDQoNCg0KU29sbyB1dGlsaXphcmVtb3MgYWxndW5hcyB2YXJpYWJsZXMgbnVtw6lyaWNhcy4gTG9zIGRhdG9zIGRlYmVuIGNvbnRlbmVyIHNvbG8gdmFyaWFibGVzIGNvbnRpbnVhcywgeWEgcXVlIGVsIGFsZ29yaXRtbyAkayQtbWVhbnMgdXRpbGl6YSBtZWRpYXMgdmFyaWFibGVzLiBEYWRvIHF1ZSBubyBxdWVyZW1vcyBxdWUgZWwgYWxnb3JpdG1vICRrJC1tZWFucyBkZXBlbmRhIGRlIHVuYSB1bmlkYWQgZGUgdmFyaWFibGUgYXJiaXRyYXJpYSwgY29tZW56YW1vcyBlc2NhbGFuZG8gbG9zIGRhdG9zIHV0aWxpemFuZG8gbGEgZnVuY2nDs24gYHNjYWxlYCBkZSBSIGRlIGxhIHNpZ3VpZW50ZSBtYW5lcmE6DQoNCg0KYGBge3J9DQpkYXQgPC0gZGF0b3NDb21wbGV0b1sxOjEwMCwgMjE6MjRdDQpkZiA8LSBzY2FsZShkYXQpDQpoZWFkKGRmKQ0KYGBgDQo8IS0tICUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUgLS0+DQo8IS0tIFNlcGFyYWRvciAtLT4NCg0KIyBFamVtcGxvOiBgc3RhdHM6OmttZWFuc2AgeSBgZmFjdG9yZXh0cmFgDQoNCiMjIyBEZXNjcmlwY2nDs24gZGUgbGEgZnVuY2nDs24gYHN0YXRzOjprbWVhbnNgIA0KDQpVc2FyZW1vcyBsYSBmdW5jacOzbiBga21lYW5zYCBkZWwgcGFxdWV0ZSBgc3RhdHNgLiBVbiBmb3JtYXRvIHNpbXBsZSBlczogDQoNCmBgYHtyLCBldmFsPUZBTFNFfQ0Ka21lYW5zKFgsIGNlbnRlcnMsIGl0ZXIubWF4ID0gMTAsIG5zdGFydCA9IDEpDQpgYGANCg0KQXF1w606IA0KICANCiAgKyBgWGA6IGVzIHVuYSBtYXRyaXogbnVtw6lyaWNhLCB1biBkYXRhIGZyYW1lIG51bcOpcmljbyBvIHVuIHZlY3RvciBudW3DqXJpY28uDQogIA0KICArIGBjZW50ZXJzYDogbG9zIHBvc2libGVzIHZhbG9yZXMgc29uIGVsIG7Dum1lcm8gZGUgYWdydXBhY2lvbmVzICgkayQpIG8gdW4gY29uanVudG8gZGUgY2VudHJvcyBkZSBhZ3J1cGFjacOzbiBpbmljaWFsZXMgKGRpc3RpbnRvcykuIFNpIHNlIGVzcGVjaWZpY2EgdW4gbsO6bWVybywgc2UgZWxpZ2UgdW4gY29uanVudG8gYWxlYXRvcmlvIGRlIGZpbGFzIChkaXN0aW50YXMpIGVuIGB4YCBjb21vIGxvcyBjZW50cm9zIGluaWNpYWxlcy4NCiAgDQogICsgYGl0ZXIubWF4YDogbsO6bWVybyBkZSBtw6F4aW1vIGRlIGl0ZXJhY2lvbmVzIHBlcm1pdGlkaWFzLiBQb3IgZGVmZWN0byBlcyAxMC4gDQogIA0KICArIGBuc3RhcnRgOiBFbCBuw7ptZXJvIGRlIHBhcnRpY2lvbmVzIGluaWNpYWxlcyBhbGVhdG9yaWFzIGN1YW5kbyBsb3MgY2VudHJvcyBlcyB1biBuw7ptZXJvLiBJbnRlbnRhciBgbnN0YXJ0YCA+IDEgZXMgYSBtZW51ZG8gcmVjb21lbmRhZG8uDQogIA0KPCEtLSAlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlIC0tPg0KPCEtLSBTZXBhcmFkb3IgLS0+DQoNCiMjIyBMYSBmdW5jacOzbiBgZmFjdG9yZXh0cmFgICAgDQoNCg0KUGFyYSBjcmVhciB1biBncsOhZmljbyBhdHJhY3Rpdm8gZGUgbG9zIGNvbmdsb21lcmFkb3MgZ2VuZXJhZG9zIGNvbiBsYSBmdW5jacOzbiBga21lYW5zYCwgdXRpbGl6YXJlbW9zIGVsIHBhcXVldGUgYGZhY3RvZXh0cmFgLg0KDQpgYGB7cn0NCmxpYnJhcnkoZmFjdG9leHRyYSkNCmBgYA0KDQoNCjwhLS0gJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSAtLT4NCjwhLS0gU2VwYXJhZG9yIC0tPg0KDQojIEVzdGltYW5kbyBlbCBuw7ptZXJvIMOzcHRpbW8gZGUgY2zDunN0ZXJzLg0KDQojIyMgUHJlZ3VudGEgY2VudHJhbA0KDQpFbCBwcm9jZXNvIGRlIGFncnVwYW1pZW50byAkayQtbWVhbnMgcmVxdWllcmUgcXVlIGxvcyB1c3VhcmlvcyBpbmRpcXVlbiBjdcOhbnRvcyBncnVwb3MgZGVzZWFuIGdlbmVyYXIuIFVuYSBwcmVndW50YSBjZW50cmFsIGVzOiAqwr9Dw7NtbyBzZWxlY2Npb25hciBlbCBuw7ptZXJvIGFkZWN1YWRvIGRlIGdydXBvcyAoJGskKT8qDQoNCg0KPCEtLSAlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlIC0tPg0KPCEtLSBTZXBhcmFkb3IgLS0+DQoNCiMjIyBSZXNwdWVzdGEgYSBwcmVndW50YSBjZW50cmFsDQoNCjEuIENvbnNpc3RlIGVuIHJlYWxpemFyIGVsIGFncnVwYW1pZW50byAkayQtbWVhbnMgdXRpbGl6YW5kbyBkaWZlcmVudGVzIHZhbG9yZXMgZGUgJGskLiANCg0KMi4gTHVlZ28sIHNlIGdyYWZpY2EgbGEgc3VtYSBkZSBsb3MgY3VhZHJhZG9zIGludGVybm9zICh3c3MpIGVuIGZ1bmNpw7NuIGRlbCBuw7ptZXJvIGRlIGdydXBvcy4gDQoNCjMuIE5vcm1hbG1lbnRlLCBsYSB1YmljYWNpw7NuIGRlIHVuIHF1aWVicmUgKHB1bnRvIGRlIGluZmxleGnDs24pIGVuIGVsIGdyw6FmaWNvIHNlIGNvbnNpZGVyYSBjb21vIHVuIGluZGljYWRvciBkZWwgbsO6bWVybyBhcHJvcGlhZG8gZGUgZ3J1cG9zLg0KDQo8IS0tICUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUgLS0+DQo8IS0tIFNlcGFyYWRvciAtLT4NCg0KIyMjIENvbiBSDQoNCkxhIGZ1bmNpw7NuICBgZnZpel9uYmNsdXN0YCBkZWwgcGFxdWV0ZSBgZmFjdG9leHRyYWAgYnJpbmRhIHVuYSBzb2x1Y2nDs24gcHLDoWN0aWNhIHBhcmEgZXN0aW1hciBlbCBuw7ptZXJvIMOzcHRpbW8gZGUgZ3J1cG9zLg0KDQpgYGB7cn0NCmxpYnJhcnkoZmFjdG9leHRyYSkNCmZ2aXpfbmJjbHVzdChkZiwga21lYW5zLCBtZXRob2QgPSAid3NzIikgKw0KZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gNCwgbGluZXR5cGUgPSAyKQ0KYGBgDQoNCkVsIGdyw6FmaWNvIGlsdXN0cmEgY8OzbW8gbGEgdmFyaWFiaWxpZGFkIGRlbnRybyBkZSBsb3MgZ3J1cG9zIGNhbWJpYSBjb24gZWwgbsO6bWVybyBkZSBncnVwb3MsICRrJC4gRXN0YSB2YXJpYWJpbGlkYWQgZGlzbWludXllIGNvbiAkayQsIHBlcm8gc2Ugbm90YSB1biBwdW50byBkZSBpbmZsZXhpw7NuIG8gImNvZG8iIGVuICRrID0gNCQuIEVzdGUgcHVudG8gc3VnaWVyZSBxdWUgYWdyZWdhciBtw6FzIGdydXBvcyBkZXNwdcOpcyBkZWwgY3VhcnRvIG5vIGFwb3J0YSBtdWNobyB2YWxvci4gRW4gbGEgc2lndWllbnRlIHNlY2Npw7NuLCBwcm9jZWRlcmVtb3MgYSBjbGFzaWZpY2FyIGxhcyBvYnNlcnZhY2lvbmVzIGVuIDQgZ3J1cG9zLg0KDQoNCjwhLS0gJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSAtLT4NCjwhLS0gU2VwYXJhZG9yIC0tPg0KDQojIENhbGN1bGFuZG8gZWwgYWdydXBhbWllbnRvICRrJC1tZWFucw0KDQojIyMgYHNldC5zZWVkYA0KDQoxLiBFbCBhbGdvcml0bW8gZGUgYWdydXBhbWllbnRvICRrJC1tZWFucyBjb21pZW56YSBzZWxlY2Npb25hbmRvIGsgY2VudHJvaWRlcyBkZSBtYW5lcmEgYWxlYXRvcmlhLCBwb3IgbG8gcXVlIGVzIHJlY29tZW5kYWJsZSB1c2FyIGxhIGZ1bmNpw7NuIGBzZXQuc2VlZGAgcGFyYSBmaWphciB1bmEgc2VtaWxsYSBlbiBlbCBnZW5lcmFkb3IgZGUgbsO6bWVyb3MgYWxlYXRvcmlvcyBkZSBSLiANCg0KMi4gRXN0byBnYXJhbnRpemEgcXVlIGxvcyByZXN1bHRhZG9zIHNlYW4gcmVwcm9kdWNpYmxlcywgZGUgbW9kbyBxdWUgY3VhbHF1aWVyIGxlY3RvciBkZSBlc3RlIGFydMOtY3VsbyBvYnRlbmdhIGxvcyBtaXNtb3MgcmVzdWx0YWRvcyBxdWUgc2UgbXVlc3RyYW4gYSBjb250aW51YWNpw7NuLg0KDQo8IS0tICUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUgLS0+DQo8IS0tIFNlcGFyYWRvciAtLT4NCg0KIyMjIGBrbWVhbnNgDQoNCkVsIGPDs2RpZ28gUiBhIGNvbnRpbnVhY2nDs24gZWplY3V0YSBlbCBhZ3J1cGFtaWVudG8gJGskLW1lYW5zIGNvbiAkayA9IDQkOg0KDQpgYGB7cn0NCiNDYWxjdWxhciBrLW1lYW5zIGNvbiBrID0gNA0KDQpzZXQuc2VlZCgxMjMpDQprIDwtNA0Ka20ucmVzIDwtIGttZWFucyhkZiwgY2VudGVycyA9IGssIG5zdGFydCA9IDI1KQ0KYGBgDQoNCg0KPCEtLSAlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlIC0tPg0KPCEtLSBTZXBhcmFkb3IgLS0+DQoNCiMjIyBgbnN0YXJ0YA0KDQoxLiBEZWJpZG8gYSBxdWUgZWwgcmVzdWx0YWRvIGZpbmFsIGRlbCBhZ3J1cGFtaWVudG8gJGskLW1lYW5zIGRlcGVuZGUgZGUgbGFzIGFzaWduYWNpb25lcyBpbmljaWFsZXMgYWxlYXRvcmlhcywgZXNwZWNpZmljYW1vcyBgbnN0YXJ0ID0gMjVgLiANCg0KMi4gRXN0byBzaWduaWZpY2EgcXVlIFIgcHJvYmFyw6EgMjUgYXNpZ25hY2lvbmVzIGluaWNpYWxlcyBhbGVhdG9yaWFzIGRpZmVyZW50ZXMgeSBzZWxlY2Npb25hcsOhIGxvcyBtZWpvcmVzIHJlc3VsdGFkb3MgYmFzYWRvcyBlbiBsYSB2YXJpYWNpw7NuIGludHJhLWNsdXN0ZXIgbcOhcyBiYWphLiANCg0KMy4gQXVucXVlIGVsIHZhbG9yIHByZWRldGVybWluYWRvIGRlIGBuc3RhcnRgIGVuIFIgZXMgdW5vLCBlcyBhbHRhbWVudGUgcmVjb21lbmRhYmxlIHV0aWxpemFyIHVuIHZhbG9yIG3DoXMgZ3JhbmRlLCBjb21vIDI1IG8gNTAsIHBhcmEgb2J0ZW5lciByZXN1bHRhZG9zIG3DoXMgZXN0YWJsZXMuDQoNCg0KPCEtLSAlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlIC0tPg0KPCEtLSBTZXBhcmFkb3IgLS0+DQoNCiMjIyBSZXN1bHRhZG9zIGZpbmFsZXMNCg0KYGBge3J9DQpwcmludChrbS5yZXMpDQpgYGANCg0KDQo8IS0tICUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUgLS0+DQo8IS0tIFNlcGFyYWRvciAtLT4NCg0KIyMjIEludGVycHJldGFjaW9uZXMNCg0KTGEgc2FsaWRhIGltcHJlc2EgcHJlc2VudGE6DQoNCjEuICoqTG9zIHByb21lZGlvcyBvIGNlbnRyb3MgZGUgbG9zIGdydXBvcyoqOiB1bmEgbWF0cml6IGVuIGxhIHF1ZSBsYXMgZmlsYXMgcmVwcmVzZW50YW4gZWwgbsO6bWVybyBkZWwgZ3J1cG8gKDEgYSA0KSB5IGxhcyBjb2x1bW5hcyByZXByZXNlbnRhbiBsYXMgdmFyaWFibGVzLg0KDQoyLiAqKkVsIHZlY3RvciBkZSBhZ3J1cGFtaWVudG8qKjogVW4gY29uanVudG8gZGUgbsO6bWVyb3MgZW50ZXJvcyAoZGUgMSBhICRrJCkgcXVlIHNlw7FhbGEgZWwgZ3J1cG8gYWwgcXVlIHNlIGFzaWduYSBjYWRhIHB1bnRvLg0KDQo8IS0tICUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUgLS0+DQo8IS0tIFNlcGFyYWRvciAtLT4NCg0KIyBNZWRpYXMgcG9yIGNsw7pzdGVycw0KDQojIyMgTWVkaWFzOiBPYnNlcnZhY2nDs24NCg0KRXMgcG9zaWJsZSBjYWxjdWxhciBsYSBtZWRpYSBkZSBjYWRhIHZhcmlhYmxlICAoYFNlbUFjdW1gLCBgRXhhbTFgLCBgRXhhbTJgIHkgYEV4YW0zYCkgcG9yIGNsw7pzdGVyIChgMWAsIGAyYCwgYDNgLCBgNGApIHVzYW5kbyBsb3MgZGF0b3Mgb3JpZ2luYWxlcy4gRW4gbGFzIHNpZ3VpZW50ZXMgc2VjY2lvbmVzLCBjYWxjdWxhcmVtb3MgZXNhcyAxNiBtZWRpYXMgcGFyYSBsb3MgZGF0b3M6IA0KDQoxLiBTaSBlc2NhbGFyIChgZGF0YCkuDQoNCjIuIEVzY2FsYWRvcyAoYGRmYCkuIA0KDQo8IS0tICUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUgLS0+DQo8IS0tIFNlcGFyYWRvciAtLT4NCg0KIyMjIFVzYW5kbyBsb3MgZGF0b3Mgc2luIGVzY2FsYXIgKGBkYXRgKQ0KDQoNCmBgYHtyfQ0KYWdncmVnYXRlKGRhdCwgYnk9bGlzdChjbHVzdGVyPWttLnJlcyRjbHVzdGVyKSwgbWVhbikNCmBgYA0KDQoNCjwhLS0gJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSAtLT4NCjwhLS0gU2VwYXJhZG9yIC0tPg0KDQojIyMgVXNhbmRvIGxvcyBkYXRvcyBlc2NhbGFkb3MgKGBkZmApDQoNCk9ic2VydmUgcXVlIGxvcyByZXN1bHRhZG9zIHF1ZSBzZSBtdWVzdHJhbiBhYmFqbyBjb2luY2lkZW4gY29uIGVsIG91dHB1dCBkZSBga20ucmVzYC4gDQoNCmBgYHtyfQ0KYWdncmVnYXRlKGRmLCBieT1saXN0KGNsdXN0ZXI9a20ucmVzJGNsdXN0ZXIpLCBtZWFuKQ0KYGBgDQoNCg0KPCEtLSAlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlIC0tPg0KPCEtLSBTZXBhcmFkb3IgLS0+DQoNCiMgQ2xhc2lmaWNhciBvYnNlcnZhY2lvbmVzIHBvciBjbMO6c3RlcnMNCg0KDQpTaSBzZSBkZXNlYSBhZ3JlZ2FyIGxhcyBjbGFzaWZpY2FjaW9uZXMgZGUgbG9zIHB1bnRvcyBhIGxvcyBkYXRvcyBvcmlnaW5hbGVzLCBzZSBwdWVkZSBlamVjdXRhciBlc3RlIGPDs2RpZ286DQoNCmBgYHtyfQ0KZGYuY2xhc2lmIDwtIGNiaW5kKGRhdCwgY2x1c3RlciA9IGttLnJlcyRjbHVzdGVyKQ0KaGVhZChkZi5jbGFzaWYpDQpgYGANCg0KPCEtLSAlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlIC0tPg0KPCEtLSBTZXBhcmFkb3IgLS0+DQoNCiMgT3V0cHV0IGRlIGBrbWVhbnNgDQoNCiMjIyBga21lYW5zYDogdmFsb3JlcyBxdWUgc2UgcHVlZGVuIG9idGVuZXINCg0KTGEgZnVuY2nDs24gYGttZWFuc2AgZGV2dWVsdmUgdW5hIGxpc3RhIGRlIGNvbXBvbmVudGVzIHF1ZSBpbmNsdXllbjoNCg0KMS4gYGNsdXN0ZXJgOiBVbiB2ZWN0b3IgZGUgZW50ZXJvcyAoZGUgMSBhICRrJCkgcXVlIGluZGljYSBlbCBjbMO6c3RlciBhbCBxdWUgc2UgYXNpZ25hIGNhZGEgcHVudG8uDQoNCjIuIGBjZW50ZXJzYDogVW5hIG1hdHJpeiBkZSBjZW50cm9zIGRlIGNsw7pzdGVyIChtZWRpYXMgZGUgY2zDunN0ZXIpLg0KDQozLiBgdG90c3NgOiBMYSBzdW1hIHRvdGFsIGRlIGN1YWRyYWRvcyB5IG1pZGUgbGEgdmFyaWFiaWxpZGFkIHRvdGFsIGVuIHRvZG9zIGxvcyBkYXRvcyAoc2luIHBhcnRpY2lvbmFyIGxvcyBkYXRvcyBvcmlnaW5hbGVzLCBjb21vIHNpIHNvbG8gdHV2acOpc2Vtb3MgdW4gc29sbyBjbMO6c3RlcikuIEVzIGRlY2lyLCANCiQkXHRleHR7VG90c3N9IFw7ID1cOyBcc3VtXGxpbWl0c197aT0xfV5uKHhfaSAtIFxvdmVybGluZXt4fSleMiQkDQoNCjQuIGB3aXRoaW5zc2A6IFZlY3RvciBkZSBzdW1hIGRlIGN1YWRyYWRvcyBkZW50cm8gZGVsIGNsw7pzdGVyLCBjb24gdW4gY29tcG9uZW50ZSBwb3IgY2zDunN0ZXIuIEVzIGRlY2lyLCANCg0KJCRcdGV4dHtXaXRoaW5zc31fayBcLD1cOyBXKENfaykgXDs9XDsgXHN1bVxsaW1pdHNfe3hfaSBcaW4gQ19rfSAoeF9pIC1cb3ZlcmxpbmV7eH1fayleMiQkDQoNCjUuIGB0b3Qud2l0aGluc3NgOiBTdW1hIHRvdGFsIGRlIGN1YWRyYWRvcyBkZW50cm8gZGVsIGNsw7pzdGVyLCBlcyBkZWNpciwgDQoNCiQkXHRleHR7VG90LndpdGhpbnNzfSBcLD1cO1xzdW1cbGltaXRzX3trPTF9XksgVyhDX2spIFw7PVw7IFxzdW1cbGltaXRzX3trPTF9Xktcc3VtXGxpbWl0c197eF9pIFxpbiBDX2t9ICh4X2kgLVxvdmVybGluZXt4fV9rKV4yJCQNCg0KNi4gYGJldHdlZW5zc2A6IExhIHN1bWEgZGUgY3VhZHJhZG9zIGVudHJlIGNsw7pzdGVyZXMsIGVzIGRlY2lyLCANCg0KJCRcdGV4dHtCZXR3ZWVuc3N9IFw7ID0gXDsgXHRleHR7VG90c3N9IFwsLVwsICBcdGV4dHtUb3Qud2l0aGluc3N9JCQNCg0KNy4gYHNpemVgOiBFbCBuw7ptZXJvIGRlIG9ic2VydmFjaW9uZXMgZW4gY2FkYSBjbMO6c3Rlci4NCg0KDQo8IS0tICUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUgLS0+DQo8IS0tIFNlcGFyYWRvciAtLT4NCg0KDQojIyMgYGttZWFuc2A6IGNvbiBudWVzdHJvcyBkYXRvcw0KDQpBbGd1bm9zIGRlIGVzdG9zIGNvbXBvbmVudGVzIHB1ZWRlbiBzZXIgYWNjZWRpZG9zIGRlIGxhIHNpZ3VpZW50ZSBtYW5lcmE6DQoNCg0KDQoxLiBgc2l6ZWA6IEVsIG7Dum1lcm8gZGUgb2JzZXJ2YWNpb25lcyAkbl9rJCBlbiBjYWRhIGNsw7pzdGVyICRrJC4gIE9ic2VydmU6IA0KJCRuXDs9XDsgXHN1bVxsaW1pdHNfe2s9MX1eSyBuX2sgJCQNCg0KYGBge3J9DQprbS5yZXMkc2l6ZQ0KYGBgDQoNCg0KMi4gYGNlbnRlcnNgOiBVbmEgbWF0cml6IGRlIGNlbnRyb3MgZGUgY2zDunN0ZXIgKG1lZGlhcyBkZSBjbMO6c3RlcikuDQoNCg0KYGBge3J9DQprbS5yZXMkY2VudGVycw0KYGBgDQoNCjMuIGBjbHVzdGVyYDogVW4gdmVjdG9yIGRlIGVudGVyb3MgKGRlIDEgYSAkayQpIHF1ZSBpbmRpY2EgZWwgY2zDunN0ZXIgYWwgcXVlIHNlIGFzaWduYSBjYWRhIHB1bnRvLg0KDQpgYGB7cn0NCmttLnJlcyRjbHVzdGVyDQpgYGANCg0KDQo0LiBgdG90c3NgOiBMYSBzdW1hIHRvdGFsIGRlIGN1YWRyYWRvcywgcXVlIG1pZGUgbGEgdmFyaWFiaWxpZGFkIHRvdGFsIGVuIHRvZG9zIGxvcyBkYXRvcyAoc2luIHBhcnRpY2lvbmFyIGxvcyBkYXRvcyBvcmlnaW5hbGVzLCBjb21vIHNpIHNvbG8gdHV2acOpc2Vtb3MgdW4gc29sbyBjbMO6c3RlcikuIEVzIGRlY2lyLCANCiQkXHRleHR7VG90c3N9IFw7ID1cOyBcc3VtXGxpbWl0c197aT0xfV5uICh4X2kgLSBcb3ZlcmxpbmV7eH0pXjIkJA0KDQoNCg0KYGBge3J9DQprbS5yZXMkdG90c3MNCmBgYA0KDQo1LiBgd2l0aGluc3NgOiBWZWN0b3IgZGUgc3VtYSBkZSBjdWFkcmFkb3MgZGVudHJvIGRlbCBjbMO6c3RlciwgY29uIHVuIGNvbXBvbmVudGUgcG9yIGNsw7pzdGVyLiBFcyBkZWNpciwgDQoNCiQkXHRleHR7V2l0aGluc3N9IFwsPVw7IFcoQ19rKSBcOz1cOyBcc3VtXGxpbWl0c197eF9pIFxpbiBDX2t9ICh4X2kgLVxvdmVybGluZXt4fV9rKV4yJCQNCg0KDQoNCmBgYHtyfQ0Ka20ucmVzJHdpdGhpbnNzDQpgYGANCg0KDQo2LiBgdG90LndpdGhpbnNzYDogU3VtYSB0b3RhbCBkZSBjdWFkcmFkb3MgZGVudHJvIGRlbCBjbMO6c3RlciwgZXMgZGVjaXIsIA0KDQokJFx0ZXh0e1RvdC53aXRoaW5zc30gXCw9XDtcc3VtXGxpbWl0c197az0xfV5LIFcoQ19rKSBcOz1cOyBcc3VtXGxpbWl0c197az0xfV5LXHN1bVxsaW1pdHNfe3hfaSBcaW4gQ19rfSAoeF9pIC1cb3ZlcmxpbmV7eH1fayleMiQkDQoNCg0KDQpgYGB7cn0NCmttLnJlcyR0b3Qud2l0aGluc3MNCmBgYA0KDQoNCjcuIGBiZXR3ZWVuc3NgOiBMYSBzdW1hIGRlIGN1YWRyYWRvcyBlbnRyZSBjbMO6c3RlcmVzLCBxdWUgbWlkZSBsYSB2YXJpYWNpw7NuIGRlYmlkbyBhIGxhcyBkaWZlcmVuY2lhcyBlbnRyZSBsb3MgY2VudHJvaWRlcyBkZSBsb3MgY2zDunN0ZXJlcy4gRXMgZGVjaXIsIA0KDQokJFx0ZXh0e0JldHdlZW5zc30gXDsgPSBcOyBcdGV4dHtUb3Rzc30gXCwtXCwgIFx0ZXh0e1RvdC53aXRoaW5zc30kJA0KDQoNCmBgYHtyfQ0Ka20ucmVzJGJldHdlZW5zcw0KYGBgDQoNCg0KPCEtLSAlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlIC0tPg0KPCEtLSBTZXBhcmFkb3IgLS0+DQoNCg0KIyMjIGBrbWVhbnNgOiBwcm9wb3JjacOzbiBkZSB2YXJpYW56YSBleHBsaWNhZGENCg0KTGEgcHJvcG9yY2nDs24gZGUgdmFyaWFuemEgdG90YWwgZXhwbGljYWRhIHBvciBsYSB2YXJpYWNpw7NuIGVudHJlIGxvcyBjZW50cm9pZGVzIGRlIGxvcyBjbMO6c3RlcmVzIHNlIHB1ZWRlIGNhbGN1bGFyIGFzw606DQoNCiQkXHRleHR7UHJvcC5WYXJ9XDsgPSBcO1xmcmFje1x0ZXh0e0JldHdlZW5zc30gIH17XHRleHR7VG90c3N9fSBcOyA9IFw7IDFcOy1cOyBcZnJhY3tcdGV4dHtUb3Qud2l0aGluc3N9fXtcdGV4dHtUb3Rzc319JCQNCkVuIG51ZXN0cm8gZWplbXBsbzogDQoNCiQkXHRleHR7UHJvcC5WYXJ9XDsgPSBcO1xmcmFje1x0ZXh0e0JldHdlZW5zc30gIH17XHRleHR7VG90c3N9fVx0aW1lcyAxMDAgXCxcJSBcOyA9IFw7IFxmcmFjezE4NC44NDU4fXszOTZ9XHRpbWVzIDEwMCBcLFwlIFw7ID0gXDsgNDYuNjhcLFwlJCQNCg0KDQpTZSByZXNhbHRhIHF1ZSBhcnJpYmEgc2UgY2FsY3Vsw7MgZWwgcG9yY2VudGFqZSBkZSB2YXJpYW56YSB0b3RhbCBleHBsaWNhZGEgcG9yIGxhIHZhcmlhY2nDs24gZW50cmUgbG9zIGNlbnRyb2lkZXMgZGUgbG9zIGNsw7pzdGVyZXMuIEVuIFIgc2UgcHVlZGUgY2FsY3VsYXIgY29tbyBzZSBpbmRpY2EgYWJham8uIDogDQoNCg0KDQpgYGB7cn0NCihrbS5yZXMkYmV0d2VlbnNzL2ttLnJlcyR0b3RzcykqMTAwDQpgYGANCg0KDQoNCjwhLS0gJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSAtLT4NCjwhLS0gU2VwYXJhZG9yIC0tPg0KDQojIFZpc3VhbGl6YW5kbyBsb3MgY2zDunN0ZXJzICRrJC1tZWFucw0KDQojIyMgSW50cm9kdWNjacOzbg0KDQoxLiBFcyByZWNvbWVuZGFibGUgZ3JhZmljYXIgbG9zIHJlc3VsdGFkb3MgZGUgbG9zIGNsw7pzdGVyZXMsIHlhIHF1ZSBlc3RvIHBlcm1pdGUgZXZhbHVhciBsYSBlbGVjY2nDs24gZGVsIG7Dum1lcm8gZGUgY2zDunN0ZXJlcyB5IGNvbXBhcmFyIGRpc3RpbnRvcyBhbsOhbGlzaXMgZGUgY2zDunN0ZXJlcy4gDQoNCjIuIEFob3JhIGRlc2VhbW9zIHZpc3VhbGl6YXIgbG9zIGRhdG9zIGVuIHVuIGdyw6FmaWNvIGRlIGRpc3BlcnNpw7NuLCBjb2xvcmVhbmRvIGNhZGEgcHVudG8gc2Vnw7puIHN1IGFzaWduYWNpw7NuIGFsIGNsw7pzdGVyIGNvcnJlc3BvbmRpZW50ZS4NCg0KMy4gRWwgcmV0byBzdXJnZSBjdWFuZG8gbG9zIGRhdG9zIHRpZW5lbiBtw6FzIGRlIGRvcyB2YXJpYWJsZXMsIGxvIHF1ZSBwbGFudGVhIGxhIHByZWd1bnRhIGRlIHF1w6kgdmFyaWFibGVzIHNlbGVjY2lvbmFyIHBhcmEgZWwgZ3LDoWZpY28gZGUgZGlzcGVyc2nDs24gZW4gbG9zIGVqZXMgeCBlIHkuDQoNCjQuIFVuYSBzb2x1Y2nDs24gZXMgYXBsaWNhciB1biBhbGdvcml0bW8gZGUgcmVkdWNjacOzbiBkZSBkaW1lbnNpb25hbGlkYWQsIGNvbW8gZWwgKkFuw6FsaXNpcyBkZSBDb21wb25lbnRlcyBQcmluY2lwYWxlcyogKFBDQSksIHF1ZSB0cmFuc2Zvcm1hIGxhcyBjdWF0cm8gdmFyaWFibGVzIGVuIGRvcyBudWV2YXMgdmFyaWFibGVzIChxdWUgcmVwcmVzZW50YW4gYSBsYXMgb3JpZ2luYWxlcykgcXVlIHB1ZWRlbiB1c2Fyc2UgcGFyYSBlbCBncsOhZmljby4NCg0KNS4gRW4gb3RyYXMgcGFsYWJyYXMsIHNpIGRpc3BvbmVtb3MgZGUgdW4gY29uanVudG8gZGUgZGF0b3MgbXVsdGlkaW1lbnNpb25hbCwgdW5hIHNvbHVjacOzbiBlcyByZWFsaXphciB1biAqQW7DoWxpc2lzIGRlIENvbXBvbmVudGVzIFByaW5jaXBhbGVzKiAoUENBKSB5IGdyYWZpY2FyIGxvcyBwdW50b3MgZGUgZGF0b3Mgc2Vnw7puIGxhcyBjb29yZGVuYWRhcyBkZSBsb3MgZG9zIHByaW1lcm9zIGNvbXBvbmVudGVzIHByaW5jaXBhbGVzLiANCg0KPCEtLSAlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlIC0tPg0KPCEtLSBTZXBhcmFkb3IgLS0+DQoNCg0KIyMjIEFwbGljYW5kbyBgZmFjdG9yZXh0cmE6OmZ2aXpfY2x1c3RlcmANCg0KMS4gTGEgZnVuY2nDs24gYGZ2aXpfY2x1c3RlcmAgZGVsIHBhcXVldGUgYGZhY3RvZXh0cmFgIHNlIHB1ZWRlIHV0aWxpemFyIHBhcmEgdmlzdWFsaXphciBmw6FjaWxtZW50ZSBsb3MgY2zDunN0ZXJlcyBrLW1lYW5zLiANCg0KMi4gRXN0YSBmdW5jacOzbiB0b21hIGxvcyByZXN1bHRhZG9zIGRlIGstbWVhbnMgeSBsb3MgZGF0b3Mgb3JpZ2luYWxlcyBjb21vIGFyZ3VtZW50b3MuIA0KDQozLiBFbiBlbCBncsOhZmljbyByZXN1bHRhbnRlLCBsYXMgb2JzZXJ2YWNpb25lcyBzZSByZXByZXNlbnRhbiBtZWRpYW50ZSBwdW50b3MsIHV0aWxpemFuZG8gY29tcG9uZW50ZXMgcHJpbmNpcGFsZXMgc2kgZWwgbsO6bWVybyBkZSB2YXJpYWJsZXMgZXMgc3VwZXJpb3IgYSAyLiANCg0KYGBge3J9DQpmdml6X2NsdXN0ZXIoa20ucmVzLCBkYXRhPWRmLA0KICAgICAgICAgICAgIHBhbGV0dGUgPSBjKCJibHVlIiwgImRhcmtvcmFuZ2U0IiwgInJlZCIsICJtYWdlbnRhNCIpLA0KICAgICAgICAgICAgIHhsYWIgPSBGQUxTRSwgDQogICAgICAgICAgICAgeWxhYiA9IEZBTFNFLCANCiAgICAgICAgICAgICBnZW9tPSJwb2ludCIpDQpgYGANCg0KDQo0LiBUYW1iacOpbiBlcyBwb3NpYmxlIGRpYnVqYXIgdW5hIGVsaXBzZSBkZSBjb25jZW50cmFjacOzbiBhbHJlZGVkb3IgZGUgY2FkYSBjbMO6c3Rlci4NCg0KYGBge3J9DQpmdml6X2NsdXN0ZXIoa20ucmVzLCBkYXRhID0gZGYsDQogICAgICAgICAgICBwYWxldHRlID0gYygiYmx1ZSIsICJkYXJrb3JhbmdlNCIsICJyZWQiLCAibWFnZW50YTQiKSwNCiAgICAgICAgICAgIGVsbGlwc2UudHlwZSA9ICJldWNsaWQiLCAjIEVsaXBzZSBkZSBjb25jZW50cmFjacOzbg0KICAgICAgICAgICAgc3Rhci5wbG90ID0gVFJVRSwgICAgICAgICMgU2VnbWVudG9zIGRlc2RlIGNlbnRyb2lkZSBhIMOtdGVtcw0KICAgICAgICAgICAgcmVwZWwgPSBUUlVFLCAgICAgICAgICAgICMgRXZpdGFyIHRyYXNsYXBhbWllbnRvcw0KICAgICAgICAgICAgZ2d0aGVtZSA9IHRoZW1lX21pbmltYWwoKQ0KICAgICAgICAgICAgKQ0KYGBgDQoNCjwhLS0gJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSAtLT4NCjwhLS0gU2VwYXJhZG9yIC0tPg0KDQoNCiMgRm9ydGFsZXphcyB5IGRlYmlsaWRhZGVzIGRlbCBhbGdvcml0bW8gJGskLW1lYW5zDQoNCiMjIyBGb3J0YWxlemFzDQoNCjEuIEVsIGFsZ29yaXRtbyBkZSBjbHVzdGVyaW5nICRrJC1tZWFucyBlcyBtdXkgc2VuY2lsbG8geSByw6FwaWRvLiANCg0KMi4gRXMgZWZpY2llbnRlIHBhcmEgbWFuZWphciBjb25qdW50b3MgZGUgZGF0b3MgbXV5IGdyYW5kZXMuDQoNCjwhLS0gJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSAtLT4NCjwhLS0gU2VwYXJhZG9yIC0tPg0KDQojIyMgRGViaWxpZGFkZXMNCg0KDQpTaW4gZW1iYXJnbywgcHJlc2VudGEgYWxndW5hcyBkZWJpbGlkYWRlcywgZW50cmUgZWxsYXM6DQoNCjEuICoqUmVxdWllcmUgY29ub2NpbWllbnRvIHByZXZpbyBkZWwgbsO6bWVybyBkZSBjbHVzdGVyczoqKiBFbCBhbmFsaXN0YSBkZWJlIGVsZWdpciBlbCBuw7ptZXJvIGFkZWN1YWRvIGRlIGNsdXN0ZXJzIChrKSBkZSBhbnRlbWFuby4NCg0KMi4gKipTZW5zaWJpbGlkYWQgYSBsYSBzZWxlY2Npw7NuIGluaWNpYWwgZGUgY2VudHJvcyBkZSBjbHVzdGVyczoqKiBMb3MgcmVzdWx0YWRvcyBmaW5hbGVzIGRlcGVuZGVuIGRlIGxhIHNlbGVjY2nDs24gYWxlYXRvcmlhIGluaWNpYWwgZGUgbG9zIGNlbnRyb3MgZGUgbG9zIGNsdXN0ZXJzLiDCv1BvciBxdcOpIGVzIHVuIHByb2JsZW1hPyBQb3JxdWUgY2FkYSBlamVjdWNpw7NuIGRlbCBhbGdvcml0bW8gcHVlZGUgc2VsZWNjaW9uYXIgZGlmZXJlbnRlcyBjZW50cm9zIGluaWNpYWxlcywgbG8gcXVlIHB1ZWRlIGxsZXZhciBhIHJlc3VsdGFkb3MgZGUgY2x1c3RlcmluZyBkaXN0aW50b3MgZW4gZGlmZXJlbnRlcyBlamVjdWNpb25lcyBkZWwgbWlzbW8gY29uanVudG8gZGUgZGF0b3MuDQoNCjMuICoqU2Vuc2liaWxpZGFkIGEgbG9zIHZhbG9yZXMgYXTDrXBpY29zOioqIEVsIGFsZ29yaXRtbyBlcyB2dWxuZXJhYmxlIGEgbG9zIG91dGxpZXJzLg0KDQo0LiAqKkRlcGVuZGVuY2lhIGRlbCBvcmRlbiBkZSBsb3MgZGF0b3M6KiogU2kgc2UgcmVvcmdhbml6YW4gbG9zIGRhdG9zLCBlcyBwcm9iYWJsZSBxdWUgc2Ugb2J0ZW5nYSB1bmEgc29sdWNpw7NuIGRpZmVyZW50ZSBjYWRhIHZleiBxdWUgc2UgY2FtYmllIGVsIG9yZGVuIGRlIGxvcyBkYXRvcy4NCg0KPCEtLSAlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlIC0tPg0KPCEtLSBTZXBhcmFkb3IgLS0+DQoNCg0KIyMjIFBvc2libGVzIFNvbHVjaW9uZXMgYSBsYXMgZGViaWxpZGFkZXMNCg0KDQoxLiAqKlBhcmEgbGEgZWxlY2Npw7NuIGRlbCBuw7ptZXJvIGRlIGNsdXN0ZXJzICgkayQpOioqIEVqZWN1dGFyIGVsIGFsZ29yaXRtbyBLLW1lYW5zIHBhcmEgdW4gcmFuZ28gZGUgdmFsb3JlcyBkZSBrLCBwb3IgZWplbXBsbywgdmFyaWFuZG8gayBlbnRyZSAyIHkgMTAuIEx1ZWdvLCBlbGVnaXIgZWwgbWVqb3IgJGskIGNvbXBhcmFuZG8gbG9zIHJlc3VsdGFkb3MgZGUgY2x1c3RlcmluZyBvYnRlbmlkb3MgcGFyYSBsb3MgZGlmZXJlbnRlcyB2YWxvcmVzIGRlICRrJC4NCg0KMi4gKipQYXJhIGxhIHNlbnNpYmlsaWRhZCBhIGxhIHNlbGVjY2nDs24gaW5pY2lhbCBkZSBjZW50cm9zOioqIEVqZWN1dGFyIGVsIGFsZ29yaXRtbyBLLW1lYW5zIHZhcmlhcyB2ZWNlcyBjb24gZGlmZXJlbnRlcyBjZW50cm9zIGRlIGNsdXN0ZXJzIGluaWNpYWxlcy4gU2Ugc2VsZWNjaW9uYSBjb21vIHNvbHVjacOzbiBmaW5hbCBsYSBlamVjdWNpw7NuIGNvbiBsYSBtZW5vciBzdW1hIHRvdGFsIGRlIGN1YWRyYWRvcyBkZW50cm8gZGVsIGNsdXN0ZXIuDQoNCjMuICoqUGFyYSBtaXRpZ2FyIGxhIGluZmx1ZW5jaWEgZGUgbG9zIG91dGxpZXJzOioqIFV0aWxpemFyIGVsIGFsZ29yaXRtbyBQQU0gKFBhcnRpdGlvbmluZyBBcm91bmQgTWVkb2lkcyksIHF1ZSBlcyBtZW5vcyBzZW5zaWJsZSBhIGxvcyB2YWxvcmVzIGF0w61waWNvcy4NCg0KDQo8IS0tICUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUgLS0+DQo8IS0tIFNlcGFyYWRvciAtLT4NCg0KDQojIEFsdGVybmF0aXZhIGFsIGFncnVwYW1pZW50byAkayQtbWVhbnMNCg0KMS4gVW5hIGFsdGVybmF0aXZhIHJvYnVzdGEgYWwgJGskLW1lYW5zIGVzIGVsIFBBTSAoUGFydGl0aW9uaW5nIEFyb3VuZCBNZWRvaWRzKSwgcXVlIHNlIGJhc2EgZW4gbWVkb2lkZXMuIA0KDQoyLiBFbCBhZ3J1cGFtaWVudG8gUEFNIHB1ZWRlIGNhbGN1bGFyc2UgdXRpbGl6YW5kbyBsYSBmdW5jacOzbiBgcGFtYCBkZWwgcGFxdWV0ZSBgY2x1c3RlcmAuIA0KDQozLiBMYSBmdW5jacOzbiBgcGFta2AgZGVsIHBhcXVldGUgYGZwY2AgZXMgdW4gZW52b2x0b3JpbyBwYXJhIFBBTSBxdWUgdGFtYmnDqW4gaW1wcmltZSBlbCBuw7ptZXJvIHN1Z2VyaWRvIGRlIGNsdXN0ZXJzIGJhc2FkbyBlbiBlbCBhbmNobyBwcm9tZWRpbyDDs3B0aW1vIGRlIGxhIHNpbHVldGEuDQoNCjwhLS0gJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSAtLT4NCjwhLS0gU2VwYXJhZG9yIC0tPg0KDQojIFJlc3VtZW4gDQoNCjEuIEVsIGFncnVwYW1pZW50byAkayQtbWVhbnMgcHVlZGUgdXNhcnNlIHBhcmEgY2xhc2lmaWNhciBvYnNlcnZhY2lvbmVzIGVuICRrJCBncnVwb3MsIGJhc8OhbmRvc2UgZW4gc3Ugc2ltaWxpdHVkLiANCg0KMi4gQ2FkYSBncnVwbyBlc3TDoSByZXByZXNlbnRhZG8gcG9yIGVsIHZhbG9yIG1lZGlvIGRlIGxvcyBwdW50b3MgZW4gZWwgZ3J1cG8sIGNvbm9jaWRvIGNvbW8gZWwgKmNlbnRyb2lkZSogZGVsIGNsdXN0ZXIuDQoNCjMuIEVsIGFsZ29yaXRtbyAkayQtbWVhbnMgcmVxdWllcmUgcXVlIGxvcyB1c3VhcmlvcyBlc3BlY2lmaXF1ZW4gZWwgbsO6bWVybyBkZSBjbHVzdGVycyBhIGdlbmVyYXIuIA0KDQo0LiBMYSBmdW5jacOzbiBga21lYW5zYCBkZWwgcGFxdWV0ZSBgc3RhdHNgIGVuIFIgcHVlZGUgdXRpbGl6YXJzZSBwYXJhIGNhbGN1bGFyIGVsIGFsZ29yaXRtbyAkazQtbWVhbnMuDQoNCjUuIEVsIGZvcm1hdG8gc2ltcGxpZmljYWRvIGVzIGBrbWVhbnMoeCwgY2VudGVycylgLCBkb25kZSBgeGAgZXMgZWwgY29uanVudG8gZGUgZGF0b3MgeSBgY2VudGVyc2AgZXMgZWwgbsO6bWVybyBkZSBjbHVzdGVycyBhIHByb2R1Y2lyLg0KDQo2LiBEZXNwdcOpcyBkZSBjYWxjdWxhciBlbCBhZ3J1cGFtaWVudG8gJGskLW1lYW5zLCBsYSBmdW5jacOzbiBgZnZpel9jbHVzdGVyYCBkZWwgcGFxdWV0ZSBgZmFjdG9leHRyYWAgcHVlZGUgdXNhcnNlIHBhcmEgdmlzdWFsaXphciBsb3MgcmVzdWx0YWRvcy4gDQoNCjcuIEVsIGZvcm1hdG8gZXMgYGZ2aXpfY2x1c3RlcihrbS5yZXMsIGRhdGEpYCwgZG9uZGUgYGttLnJlc2Agc29uIGxvcyByZXN1bHRhZG9zIGRlICRrJC1tZWFucyB5IGRhdGEgY29ycmVzcG9uZGUgYWwgY29uanVudG8gZGUgZGF0b3Mgb3JpZ2luYWwuDQoNCg0KPCEtLSAlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlIC0tPg0KPCEtLSBDYXDDrXR1bG8gRWplcmNpY2lvcyAtLT4NCg0KIyBFamVyY2ljaW9zDQoNCg0KDQoNCjwhLS0gJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSAtLT4NCjwhLS0gU2VwYXJhZG9yIC0tPg0KDQojIyMgRWplcmNpY2lvIDENCg0KDQpgYGB7ciBldmFsPUZBTFNFLCBlY2hvPUZBTFNFfQ0KI2h0dHBzOi8vZGV2ZWxvcGVyLmlibS5jb20vdHV0b3JpYWxzL2F3Yi1jbHVzdGVyLWFuYWx5c2lzLWluLXIvDQpgYGANCg0KQW5hbGljZW1vcyBlbCBzaWd1aWVudGUgY29uanVudG8gZGUgZGF0b3MuIFBhcmEgc3UgY3JlYWNpw7NuLCBnZW5lcmFtb3MgdHJlcyBjbHVzdGVycyB1dGlsaXphbmRvIGRpc3RyaWJ1Y2lvbmVzIG5vcm1hbGVzLiBDYWRhIHB1bnRvIGRlIGRhdG9zIGluY2x1eWUgdHJlcyBjYXJhY3RlcsOtc3RpY2FzOiB1biB2YWxvciBgeGAgZGUgdW5hIHZhcmlhYmxlICRYJCAgLCB1biB2YWxvciBgeWAgZGUgJFkkLCB5IHVuYSBgY2xhc2VgIHF1ZSBldGlxdWV0YSBlbCBwdW50by4gRXhpc3RlbiB0cmVzIGNsYXNlcyBlbiBlc3RlIGNvbmp1bnRvIGRlIGRhdG9zICgxLCAyIHkgMykuIEVsIG9iamV0aXZvIGRlbCBlamVyY2ljaW8gZXMgcXVlIGxvcyBhbGdvcml0bW9zIGRlIGNsdXN0ZXJpbmcgcmVwcmVzZW50ZW4gZXN0YXMgY2xhc2VzIGNvbW8gY2x1c3RlcnMuDQoNCmBgYHtyfQ0KbGlicmFyeShtdnRub3JtKQ0KDQpzZXQuc2VlZCgxMjMpDQpkYXRhc2V0IDwtIHsNCiAgICAgICAgI2NyZWFyIGxhcyAzIGRpc3RyaWJ1Y2lvbmVzDQogICAgICAgIGNsdXN0ZXIxID0gZGF0YS5mcmFtZShybXZub3JtKDQwLCBjKDAsIDApLCBkaWFnKDIpKmMoMiwgMSkpKSAlPiUgbXV0YXRlKGNsYXNzPWZhY3RvcigxKSkNCiAgICAgICAgY2x1c3RlcjIgPSBkYXRhLmZyYW1lKHJtdm5vcm0oMTAwLCBjKDMsIDMpLCBkaWFnKDIpKmMoMSwgMykpKSAlPiUgbXV0YXRlKGNsYXNzPWZhY3RvcigyKSkNCiAgICAgICAgY2x1c3RlcjMgPSBkYXRhLmZyYW1lKHJtdm5vcm0oNjAsIGMoNiwgNiksIGRpYWcoMikpKSAlPiUgbXV0YXRlKGNsYXNzPWZhY3RvcigzKSkNCiAgICAgICAgI2NyZWFyIGVsIGRhdGEgZnJhbWUNCiAgICAgICAgZGF0YSA9IGJpbmRfcm93cyhjbHVzdGVyMSwgY2x1c3RlcjIsIGNsdXN0ZXIzKQ0KICAgICAgICAjbm9tYnJlcyBkZSBsYXMgY29sdW1uYXMNCiAgICAgICAgbmFtZXMoZGF0YSkgPSBjKCJ4IiwgInkiLCAiY2xhc2UiKQ0KICAgICAgICAjcmV0b3JuYXIgbG9zIGRhdG9zDQogICAgICAgIGRhdGENCiAgICAgICAgfQ0KaGVhZChkYXRhc2V0KQ0KYGBgDQoNClBhcmEgZWxsbywgcmVhbGl6YXIgbG9zIHNpZ3VpZW50ZXMgaW5jaXNvcy4NCg0KKGEpIEdyYWZpY2FyIGxvcyBkYXRvcyBlbiB1biBkaWFncmFtYSBkZSBkaXNwZXJzacOzbiB1c2FuZG8gYGdncGxvdGAgeSBlbCBhcmd1bWVudG8gYGNvbG9yPWNsYXNzYCBwYXJhIG9ic2VydmFyIGxhIGFncnVwYWNpw7NuIG5hdHVyYWwgcXVlIHNlIGludGVudGEgcmVwbGljYXIuDQoNCihiKSBBcGxpY2FyIGxhIGZ1bmNpw7NuIGBmdml6X25iY2x1c3RgIGRlbCBwYXF1ZXRlIGBmYWN0b2V4dHJhYCAgcGFyYSBlc3RpbWFyIGVsIG7Dum1lcm8gw7NwdGltbyBkZSBncnVwb3MuDQoNCihjKSBEZWZpbmEgdW4gbnVldm8gb2JqZXRvIHF1ZSBjb250ZW5nYSBzb2xvIGxhcyB2YXJpYWJsZXMgYHhgIHkgYHlgLiANCg0KYGBge3J9DQpkZjEgPC0gZGF0YXNldFtjKCJ4IiwgInkiKV0NCmhlYWQoZGYxKQ0KYGBgDQoNCihkKSBFamVjdXRhciBlbCBhZ3J1cGFtaWVudG8gJGskLW1lYW5zIGNvbiBlbCBuw7ptZXJvIG9wdGltYWwgZGUgY2zDunN0ZXJzIG9idGVuaWRvcyBlbiBlbCBpbmNpc28gYW50ZXJpb3IgKHV0aWxpemFyIGBuc3Rhcj0yMGApLiBMTGFtZSBhbCBudWV2byBvYmpldG8gYGttLnJlczFgLg0KDQooZSkgVXRpbGl6YXIgbGEgZnVuY2nDs24gYGZ2aXpfY2x1c3RlcmAgcGFyYSB2ZXIgbG9zIHJlc3VsdGFkb3MgZGUgbGEgYWdydXBhY2nDs24gJGskLW1lYW5zLg0KDQoNCmBgYHtyLCBldmFsPUZBTFNFLCBlY2hvPUZBTFNFfQ0KbGlicmFyeShtdnRub3JtKQ0KDQpzZXQuc2VlZCgxMjMpDQpkYXRhc2V0IDwtIHsNCiAgICAgICNjcmVhciBsYXMgMyBkaXN0cmlidWNpb25lcw0KICAgICAgY2x1c3RlcjEgPSBkYXRhLmZyYW1lKHJtdm5vcm0oNDAsIGMoMCwgMCksIGRpYWcoMikgKiBjKDIsIDEpKSkgJT4lIG11dGF0ZShjbGFzcz1mYWN0b3IoMSkpDQogICAgICBjbHVzdGVyMiA9IGRhdGEuZnJhbWUocm12bm9ybSgxMDAsIGMoMywgMyksIGRpYWcoMikgKiBjKDEsIDMpKSkgJT4lIG11dGF0ZShjbGFzcz1mYWN0b3IoMikpDQogICAgICBjbHVzdGVyMyA9IGRhdGEuZnJhbWUocm12bm9ybSg2MCwgYyg2LCA2KSwgZGlhZygyKSkpICU+JSBtdXRhdGUoY2xhc3M9ZmFjdG9yKDMpKQ0KDQogICAgICAjY3JlYXIgZWwgZGF0YSBmcmFtZQ0KICAgICAgZGF0YSA9IGJpbmRfcm93cyhjbHVzdGVyMSwgY2x1c3RlcjIsIGNsdXN0ZXIzKQ0KICAgICAgI25vbWJyZXMgZGUgbGFzIGNvbHVtbmFzDQogICAgICBuYW1lcyhkYXRhKSA9IGMoIngiLCAieSIsICJjbGFzZSIpDQogICAgICAjcmV0b3JuYXIgbG9zIGRhdG9zDQogICAgICBkYXRhDQp9DQoNCmhlYWQoZGF0YXNldCkNCg0KIyhhKSBHcmFmaXF1ZSBsb3MgZGF0b3MgZW4gdW4gZGlhZ3JhbWEgZGUgZGlzcGVyc2nDs24gdXNhbmRvIGBnZ3Bsb3RgIHBhcmEgb2JzZXJ2YXIgbGEgYWdydXBhY2nDs24gbmF0dXJhbCBxdWUgc2UgaW50ZW50YSByZXBsaWNhci4NCg0KZGF0YXNldCAlPiUgZ2dwbG90KGFlcyh4PXgsIHk9eSwgY29sb3I9Y2xhc2UpKSArDQogICAgICBnZW9tX3BvaW50KCkgKw0KICAgICAgY29vcmRfZml4ZWQoKSArDQogICAgICBzY2FsZV9zaGFwZV9tYW51YWwodmFsdWVzPWMoMCwgMSwgMikpDQoNCg0KIyhiKSBBcGxpcXVlIGxhIGZ1bmNpw7NuIGBmdml6X25iY2x1c3RgIGRlbCBwYXF1ZXRlIGBmYWN0b2V4dHJhYCAgcGFyYSBlc3RpbWFyIGVsIG7Dum1lcm8gw7NwdGltbyBkZSBncnVwb3MuDQoNCmxpYnJhcnkoZmFjdG9leHRyYSkNCmZ2aXpfbmJjbHVzdChkYXRhc2V0LCBrbWVhbnMsIG1ldGhvZCA9ICJ3c3MiKSArDQpnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAzLCBsaW5ldHlwZSA9IDIpDQoNCiMoYykgRGVmaW5hIHVuIG51ZXZvIG9iamV0byBxdWUgY29udGVuZ2Egc29sbyBsYXMgdmFyaWFibGVzIGB4YCB5IGB5YC4gDQoNCmRmMSA8LSBkYXRhc2V0W2MoIngiLCAieSIpXQ0KaGVhZChkZjEpDQoNCg0KIyhkKSBFamVjdXRlIGVsIGFncnVwYW1pZW50byAkayQtbWVhbnMgY29uIGVsIG7Dum1lcm8gb3B0aW1hbCBkZSBjbMO6c3RlcnMgb2J0ZW5pZG9zIGVuIGVsIGluY2lzbyBhbnRlcmlvciAodXRpbGl6YXIgYG5zdGFyPTIwYCkuIA0KI0NhbGN1bGFyIGstbWVhbnMgY29uIGsgPSAzDQoNCmsgPC0gMw0Ka20ucmVzMSA8LSBrbWVhbnMoZGYxLCBjZW50ZXJzPWssIG5zdGFydCA9IDIwKQ0Ka20ucmVzMQ0KDQojKGUpIFV0aWxpemFyIGxhIGZ1bmNpw7NuIGBmdml6X2NsdXN0ZXJgIHBhcmEgdmVyIGxvcyByZXN1bHRhZG9zIGRlIGxhIGFncnVwYWNpw7NuICRrJC1tZWFucy4NCiMNCmZ2aXpfY2x1c3RlcihrbS5yZXMxLCBkZjEsIHhsYWIgPSBGQUxTRSwgeWxhYiA9IEZBTFNFLCBnZW9tPSJwb2ludCIpDQoNCmZ2aXpfY2x1c3RlcihrbS5yZXMxLCBkYXRhID0gZGYxLA0KICAgICAgICAgICAgcGFsZXR0ZSA9IGMoImJsdWUiLCAiZGFya29yYW5nZTQiLCAicmVkIiksDQogICAgICAgICAgICBlbGxpcHNlLnR5cGUgPSAiZXVjbGlkIiwgIyBFbGlwc2UgZGUgY29uY2VudHJhY2nDs24NCiAgICAgICAgICAgIHN0YXIucGxvdCA9IFRSVUUsICAgICAgICAjIFNlZ21lbnRvcyBkZXNkZSBjZW50cm9pZGUgYSDDrXRlbXMNCiAgICAgICAgICAgIHJlcGVsID0gVFJVRSwgICAgICAgICAgICAjIEV2aXRhciB0cmFzbGFwYW1pZW50b3MNCiAgICAgICAgICAgIGdndGhlbWUgPSB0aGVtZV9taW5pbWFsKCkNCiAgICAgICAgICAgICkNCmBgYA0KDQoNCjwhLS0gJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSAtLT4NCjwhLS0gU2VwYXJhZG9yIC0tPg0KDQojIyMgRWplcmNpY2lvIDINCg0KDQpgYGB7ciBldmFsPUZBTFNFLCBlY2hvPUZBTFNFfQ0KI2h0dHBzOi8vZGV2ZWxvcGVyLmlibS5jb20vdHV0b3JpYWxzL2F3Yi1jbHVzdGVyLWFuYWx5c2lzLWluLXIvDQpgYGANCg0KKipDb250aW51YWNpw7NuIGRlbCBlamVyY2ljaW8gMSoqLiBDb25zaWRlcmUgbGEgc2l0dWFjacOzbiBwbGFudGVhZGEgZW4gZWwgZWplcmNpY2lvIDEgeSBsb3MgcmVzdWx0YWRvcyBlbmNvbnRyYWRvcyBhbGzDrS4gDQoNCihmKSBDb25zaWRlcmUgZWwgY8OzZGlnbyBkZSBhYmFqby4gRW4gbGEgbMOtbmVhIGRlIGPDs2RpZ28gTm8uIDEsIHNlIGNvcGlhIGVsIGRhdGFzZXQgb3JpZ2luYWwgYGRhdGFzZXRgIGNvbW8gdW4gbnVldm8gZGF0YWZyYW1lIGxsYW1hZG8gYGRhdGFzZXRfa21gLiBFbiBsYSBsw61uZWEgZGUgY8OzZGlnbyBOby4gMiwgc2UgYcOxYWRlIHVuYSBudWV2YSBjb2x1bW5hIGxsYW1hZGEgYGNsYXNlX3ByZWRgIGFsIG51ZXZvIGRhdGEgZnJhbWUgYGRhdGFzZXRfa21gLiBFc3RhIGNvbHVtbmEgc2UgcmVsbGVuYSBjb24gbG9zIHZhbG9yZXMgZGUgbGFzIGV0aXF1ZXRhcyBkZSBjbMO6c3RlcmVzIHByZWRpY2hvcyBwb3IgZWwgbW9kZWxvIGRlICRrJC1tZWFucyAoYGttLnJlczEkY2x1c3RlcmApLiBMb3MgdmFsb3JlcyBzZSBjb252aWVydGVuIGVuIGZhY3RvcmVzIChjYXRlZ8Ozcmljb3MpIHVzYW5kbyBsYSBmdW5jacOzbiBgZmFjdG9yYC4gQ29tcGFyZSBsb3MgdmFsb3JlcyBvYnNlcnZhZG9zIChgY2xhc2VgKSBjb24gbG9zIHByZWRpY2hvcyAoYGNsYXNlX3ByZWRgKS4gDQoNCg0KYGBge3IsIGV2YWw9RkFMU0V9DQojMS5Db3BpYXIgZWwgZGF0YXNldCBvcmlnaW5hbCANCmRhdGFzZXRfa20gPC0gZGF0YXNldCANCg0KIzIuIEHDsWFkZSB1bmEgY29sdW1uYSBjb24gbGFzIGNsYXNlcyBwcmVkaWNoYXMNCmRhdGFzZXRfa21bJ2NsYXNlX3ByZWQnXSA9IGZhY3RvcihrbS5yZXMxJGNsdXN0ZXIpIA0KaGVhZChkYXRhc2V0X2ttKQ0KYGBgDQoNCihnKSBDb250cnVpciB1bmEgdGFibGEgZGUgZnJlY3VlbmNpYXMgYWdydXBhZGFzIGVudHJlIGxvcyB2YWxvcmVzIG9ic2VydmFkb3MgKGBjbGFzZWApIGNvbiBsb3MgcHJlZGljaG9zIChgY2xhc2VfcHJlZGApIGUgaW50ZXJwcmV0ZSBjYWRhIHVubyBkZSBsb3MgcmVzdWx0YWRvcyBlbmNvbnRyYWRvcyBlbiBsYXMgY2VsZGFzLiBFbiBwYXJ0aWN1bGFyLCB2ZXJpZmljYXIgbGEgcHJlY2lzacOzbiAoYWNjdXJhY3kpIGRlIGxhIGFncnVwYWNpw7NuLiAgDQoNCg0KKGgpIFZlcmlmaWNhciBsYSBwcmVjaXNpw7NuIChhY2N1cmFjeSkgZGUgbGEgYWdydXBhY2nDs24gZW4gdW4gZ3LDoWZpY28uIENvbXBhcmUgbnVldmFtZW50ZSBsb3MgdmFsb3JlcyBvYnNlcnZhZG9zIChgY2xhc2VgKSBjb24gbG9zIHByZWRpY2hvcyAoYGNsYXNlX3ByZWRgKS4gUHVlZGUgdXRpbGl6YXIgZWwgc2lndWllbnRlIGPDs2RpZ286ICANCg0KYGBge3IsIGV2YWw9RkFMU0V9DQojMy4gRWwgZ3LDoWZpY28NCmRhdGFzZXRfa20gJT4lIGdncGxvdChhZXMoeD14LCB5PXksIHNoYXBlPWNsYXNlLCBjb2xvcj1jbGFzZV9wcmVkKSkgKw0KICAgICAgICAgICAgICAgICAgICAgIGdlb21fcG9pbnQoKSArDQogICAgICAgICAgICAgICAgICAgICAgY29vcmRfZml4ZWQoKSArDQogICAgICAgICAgICAgICAgICAgICAgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcz1jKDAsIDEsIDIpKSArDQogICAgICAgICAgICAgICAgICAgICAgc2NhbGVfc2hhcGUoc29saWQgPSBUUlVFKQ0KYGBgDQoNCg0KYGBge3IsIGV2YWw9RkFMU0UsIGVjaG89RkFMU0V9DQojKGYpIENvbnNpZGVyZSBlbCBjw7NkaWdvIGRlIGFiYWpvLiBFbiBsYSBsw61uZWEgZGUgY8OzZGlnbyBOby4gMSwgc2UgY29waWEgZWwgZGF0YXNldCBvcmlnaW5hbCBgZGF0YXNldGAgY29tbyB1biBudWV2byBkYXRhZnJhbWUgbGxhbWFkbyBgZGF0YXNldF9rbWAuIEVuIGxhIGzDrW5lYSBkZSBjw7NkaWdvIE5vLiAyLCBzZSBhw7FhZGUgdW5hIG51ZXZhIGNvbHVtbmEgbGxhbWFkYSBgY2xhc2VfcHJlZGAgYWwgbnVldm8gZGF0YSBmcmFtZSBgZGF0YXNldF9rbWAuIEVzdGEgY29sdW1uYSBzZSByZWxsZW5hIGNvbiBsb3MgdmFsb3JlcyBkZSBsYXMgZXRpcXVldGFzIGRlIGNsw7pzdGVyZXMgcHJlZGljaG9zIHBvciBlbCBtb2RlbG8gZGUgJGskLW1lYW5zIChga20ucmVzMSRjbHVzdGVyYCkuIExvcyB2YWxvcmVzIHNlIGNvbnZpZXJ0ZW4gZW4gZmFjdG9yZXMgKGNhdGVnw7NyaWNvcykgdXNhbmRvIGxhIGZ1bmNpw7NuIGBmYWN0b3JgLiBDb21wYXJlIGxvcyB2YWxvcmVzIG9ic2VydmFkb3MgKGBjbGFzZWApIGNvbiBsb3MgcHJlZGljaG9zIChgY2xhc2VfcHJlZGApLg0KDQojZjEuQ29waWFyIGVsIGRhdGFzZXQgb3JpZ2luYWwgDQpkYXRhc2V0X2ttIDwtIGRhdGFzZXQgDQoNCiNmMi4gQcOxYWRlIHVuYSBjb2x1bW5hIGNvbiBsYXMgY2xhc2VzIHByZWRpY2hhcw0KZGF0YXNldF9rbVsnY2xhc2VfcHJlZCddID0gZmFjdG9yKGttLnJlczEkY2x1c3RlcikgDQpoZWFkKGRhdGFzZXRfa20pDQoNCiMoZykgQ29udHJ1aXIgdW5hIHRhYmxhIGRlIGZyZWN1ZW5jaWFzIGFncnVwYWRhcyBlbnRyZSBsb3MgdmFsb3JlcyBvYnNlcnZhZG9zIChgY2xhc2VgKSBjb24gbG9zIHByZWRpY2hvcyAoYGNsYXNlX3ByZWRgKSBlIGludGVycHJldGUgY2FkYSB1bm8gZGUgbG9zIHJlc3VsdGFkb3MgZW5jb250cmFkb3MgZW4gbGFzIGNlbGRhcy4gRW4gcGFydGljdWxhciwgdmVyaWZpY2FyIGxhIHByZWNpc2nDs24gKGFjY3VyYWN5KSBkZSBsYSBhZ3J1cGFjacOzbi4gIA0KDQoNCnRhYmxlKGRhdGFzZXRfa20kY2xhc2UsIGRhdGFzZXRfa20kY2xhc2VfcHJlZCkNCg0KDQojKGgpIFZlcmlmaWNhciBsYSBwcmVjaXNpw7NuIChhY2N1cmFjeSkgZGUgbGEgYWdydXBhY2nDs24gZW4gdW4gZ3LDoWZpY28uIENvbXBhcmUgbnVldmFtZW50ZSBsb3MgdmFsb3JlcyBvYnNlcnZhZG9zIChgY2xhc2VgKSBjb24gbG9zIHByZWRpY2hvcyAoYGNsYXNlX3ByZWRgKS4gUHVlZGUgdXRpbGl6YXIgZWwgc2lndWllbnRlIGPDs2RpZ286ICANCg0KIzMuIEVsIGdyw6FmaWNvDQogICAgZGF0YXNldF9rbSAlPiUgZ2dwbG90KGFlcyh4PXgsIHk9eSwgc2hhcGU9Y2xhc2UsIGNvbG9yPWNsYXNlX3ByZWQpKSArDQogICAgICBnZW9tX3BvaW50KCkgKw0KICAgICAgY29vcmRfZml4ZWQoKSArDQogICAgICBzY2FsZV9zaGFwZV9tYW51YWwodmFsdWVzPWMoMCwgMSwgMikpICsNCiAgICAgIHNjYWxlX3NoYXBlKHNvbGlkID0gVFJVRSkNCmBgYA0KDQoNCjwhLS0gJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSAtLT4NCjwhLS0gU2VwYXJhZG9yIC0tPg0KDQojIyMgRWplcmNpY2lvIDMNCg0KDQpgYGB7ciBldmFsPUZBTFNFLCBlY2hvPUZBTFNFfQ0KI2h0dHBzOi8vZGV2ZWxvcGVyLmlibS5jb20vdHV0b3JpYWxzL2F3Yi1jbHVzdGVyLWFuYWx5c2lzLWluLXIvDQpgYGANCg0KKipDb250aW51YWNpw7NuIGRlIGxvcyBlamVyY2ljaW9zIDEgeSAyKiouIENvbnNpZGVyZSBsYSBzaXR1YWNpw7NuIHBsYW50ZWFkYSBlbiBlbCBlamVyY2ljaW8gMSB5IGxvcyByZXN1bHRhZG9zIGVuY29udHJhZG9zIGVuIGVzZSBlamVyY2ljaW8geSBlbiBlbCAyLiANCg0KKipJbmNpc28gaS4qKg0KDQpJbnZlc3RpZ3VlIGVsIGNvbmNlcHRvIGRlIG1hdHJpeCBkZSBjb25mdXNpw7NuIHkgbGFzIG3DqXRyaWNhcyBxdWUgc2Ugb2J0aWVuZW4gYSBwYXJ0aXIgZGUgZWxsYS4gRXhwbGlxdWUgbGFzIG3DoXMgaW1wb3J0YW50ZXMuIFB1ZWRlIGNvbnN1bHRhciBsb3Mgc2lndWllbnRlcyBkb2N1bWVudG9zIChkZSBtaSBhdXRvcsOtYSk6IA0KICANCiAgKyBbUlB1YnMgOjogQ2FwLjE5IE1hdHJpeCBkZSBjb25mdXNpw7NuICAocGFyYSBkb3MgY2xhc2VzKV0oaHR0cHM6Ly9ycHVicy5jb20vaGxsaW5hcy9SX0xvZ2l0X0JpbmFyaW9fQ29uZnVzaW9uKS4NCiAgDQogICsgW1JQdWJzIDo6IENhcC4xOCBNYXRyaXggZGUgY29uZnVzacOzbiAgKHBhcmEgdHJlcyBjbGFzZXMpXShodHRwczovL3JwdWJzLmNvbS9obGxpbmFzL1JfTXVsdGkzX0NvbmZ1c2lvbikuDQogIA0KKippbmNpc28gai4qKiANCg0KQXBsaXF1ZSBlbCBjw7NkaWdvIGRlIGFiYWpvIHBhcmEgb2J0ZW5lciBsYSBtYXRyaXggZGUgY29uZnVzacOzbiB5IGxhcyBtw6l0cmljYXMgY29ycmVzcG9uZGllbnRlcy4gSW50ZXJwcmV0ZSBsb3MgcmVzdWx0YWRvcyBvYnRlbmlkb3MuIA0KDQoNCmBgYHtyLCBldmFsPUZBTFNFfQ0KbGlicmFyeShjYXJldCkNCmNvbmZfbWF0IDwtIGNvbmZ1c2lvbk1hdHJpeChmYWN0b3Ioa20ucmVzMSRjbHVzdGVyKSwgZmFjdG9yKGRhdGFzZXQkY2xhc2UpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtb2RlID0gImV2ZXJ5dGhpbmciLCBwb3NpdGl2ZT0iMSIpDQpjb25mX21hdA0KYGBgDQoNCg0KYGBge3IsIGV2YWw9RkFMU0UsICBlY2hvPUZBTFNFfQ0KDQojKGkpIEFwbGlxdWUgZWwgY8OzZGlnbyBkZSBhYmFqbyBwYXJhIG9idGVuZXIgbGEgbWF0cml4IGRlIGNvbmZ1c2nDs24geSBsYXMgbcOpdHJpY2FzIGNvcnJlc3BvbmRpZW50ZXMuIEludGVycHJldGUgbG9zIHJlc3VsdGFkb3Mgb2J0ZW5pZG9zLiANCg0KbGlicmFyeShjYXJldCkNCmtubl9jb25mX21hdCA8LSBjb25mdXNpb25NYXRyaXgoZmFjdG9yKGttLnJlczEkY2x1c3RlciksIGZhY3RvcihkYXRhc2V0JGNsYXNlKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9kZSA9ICJldmVyeXRoaW5nIiwgcG9zaXRpdmU9IjEiKQ0KDQprbm5fY29uZl9tYXQNCiAgICANCiAgICANCmBgYA0KDQoNCg0KDQo8IS0tICUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlIC0tPg0KDQo8IS0tIENhcMOtdHVsbyBCaWJsaW9ncmFmw61hLS0+DQoNCg0KIyBCaWJsaW9ncmFmw61hIHsudW5saXN0ZWQgLnVubnVtYmVyZWR9DQogIA0KQ29uc3VsdGFyIGVsIGRvY3VtZW50byBbUlB1YnMgOjogQW7DoWxpc2lzIG11bHRpdmFyaWFkbyAoYmlibGlvZ3JhZsOtYSldKGh0dHBzOi8vcnB1YnMuY29tL2hsbGluYXMvUl9NdWx0aXZhcmlhZG9fQmlibGlvZ3JhZmlhKS4NCg0KPCEtLSAlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSAtLT4NCg0KJm5ic3A7DQoNCg0KJm5ic3A7DQo8Y2VudGVyPg0Kfn5+DQpJZiB5b3UgZm91bmQgYW55IEVSUk9SUyBvciBoYXZlIFNVR0dFU1RJT05TLCBwbGVhc2UgcmVwb3J0IHRoZW0gdG8gbXkgZW1haWwuIFRoYW5rcy4gIA0Kfn5+DQo8L2NlbnRlcj4NCg0KDQo=