Análisis Discriminante Descriptivo
El siguiente esquema sirve de guı́a para llevar a cabo el análisis descriptivo * Determinar si el Análisis Discriminante proporciona resultados estadı́sticos, es el técnica adecuada, para la solución de su problema. * Definir los grupos y seleccionar las variables que se utilizan en el análisis (grupos y variables de interés). * Determinar si los datos son apropiados; es decir, si cumplen los supuestos. En caso de no cumplir alguno de los supuestos, determinar la gravedad del incumplimiento de los mismos, que tanto se afecta la robustez de los resultados. * Llevar a cabo el análisis y interpretar los resultados.
Se considera de importancia mencionar los objetivos en la discriminación de múltiples grupos, por lo que se presentan a continuación: * Examinar la separación de grupos en una gráfica 2D. Cuando hay más de dos grupos, se requiere más de una función discriminante para describir la separación de los grupos. * Encontrar un subconjunto de las variables originales que separen los grupos al menos tan bien como éstas. * Clasificar las variables según su aporte relativo a la separación de los grupos. Se considera con mayor validez si se trabaja la función discriminante estandarizada. * Interpretar de las dimensiones.
Aspectos previos: Grupos y variables
El Análisis Discriminante requiere de un conjunto de datos que contenga dos o más grupos mutuamente excluyentes y puntuaciones en dos o más variables para cada caso en el grupo.
Los grupos deben ser construidos de tal forma que sean las categorı́as de individuos en los cuales recae el interés diferenciación.
Por otro lado, las variables deben ser escogidas como aquellas en las que se considera o cree que los grupos van a diferir potencialmente.
El Análisis Discriminante se encargará de evaluar el grado en que dichas variables diferencian los grupos. A pesar de que no es necesario filtrar las variables, se recomienda hacerlo, teniendo presente bases teóricas o empı́ricas, para un mejor desarrollo del proceso.
Aspectos previos: Tamaño de la muestra
Se recomienda no utilizar tamaños de muestra pequeños, dado que se afecta la estabilidad y generalización de los resultados, además porque se requiere confiabilidad en los resultados que posteriormente se aplicarán a otras muestras por medio de la validación cruzada. Se recomienda que la totalidad de las muestra (\(\displaystyle\Sigma_i n_i\) ) sea al menos 10 veces el número de variables discriminantes.
Aspectos previos: Supuestos
Los datos con los cuales se trabajará deben cumplir los siguientes supuestos: * Independencia de las observaciones (vital importancia). * Normalidad multivariada. Para el análisis descriptivo no es necesaria, mas para el predictivo es indispensable. * Homogeneidad de las matrices de covarianzas. Se acepta la violación de este supuesto en si el ratio del tamaño del grupo más grande divido por el del más pequeño es menor a 1.5.
Componentes del resultado: Coeficientes
El Análisis Discriminante calcula matemáticamente los pesos para las puntuaciones en cada variable discriminadora que refleja el grado en que las puntuaciones de dicha variable difieren entre los grupos que son discriminados, las variables que más difieran tendrán mayor peso. A estos pesos se les conoce como coeficientes discriminantes.
Componentes del resultado: Función discriminantes, puntuaciones y centroides
El Análisis Discriminante forma una o más combinaciones lineales ponderadas de las variables discriminantes llamadas funciones discriminantes. La puntuación discriminante para cada función es calculada para cada caso en la muestra. Luego se calcula el puntaje discriminante promedio de los casos que pertenecen a un grupo (los centroides de los grupos) para cada función discriminante. Para fines clasificatorios y predictivos, el puntaje discriminante para cada caso posible de un grupo es comparado con el centroide del mismo, y se calcula una probabilidad de pertenencia al grupo. Entre más cercano el puntaje se encuentre al centroide de un grupo, mayor probabilidad tiene de pertenecer a éste. Los centroides de grupo revelan que tanto y de que manera los grupos son diferenciados en cada función. La magnitud en valor absoluto de los centroides de grupo indican el grado en el cual un grupo es diferenciado en una función, y el signo la dirección de diferenciación.
Función discriminante para dos grupos
Se asume que los dos grupos poseen la misma matriz de covarianza \(\Sigma\) pero diferentes vectores de media \(\mu_1, \mu_2\).
Sean \(y_{11},y_{12},...,y_{1n_1}\) y \(y_{21},y_{22},...,y_{2n_2}\) casos para los dos grupos o poblaciones, donde cada vector \(y_{ij}\) consiste en mediciones para las \(p\) variables. La función discriminante es la combinación lineal de las \(p\) variables que maximiza la distancia entre las medias de los dos grupos (transformados). \[ z_{1i} = a'y_{1i} = a_1y_{1i1}+a_2y_{1i2}+...+a_py_{1ip} \qquad i=1,...,n_1 \] \[ z_{2i} = a'y_{2i} = a_1y_{2i1}+a_2y_{2i2}+...+a_py_{2ip} \qquad i=1,...,n_2 \]
cuyas respectivas medias son \(\displaystyle\bar{z_1} = \sum_{i=1}^{n_1} \frac{z_{1i}}{n_1} = a'\bar{y_1}\) y \(\bar{z_2}=a'\bar{y_2}\).
Sea \(a\) el vector que maximiza la diferencia estandarizada al cuadrado \((\bar{z_1}-\bar{z_2})^2/s_z^2\). \[\frac{(\bar{z_1}-\bar{z_2})^2}{s_z^2} = \frac{[a'(\bar{y_1}-\bar{y_2})]^2}{a'S_{pl}a}\] el máximo ocurre en \[a = S_{pl}^{-1}(\bar{y_1}-\bar{y_2})\] o cuando \(a\) es un múltiplo de \(S_{pl}^{-1}(\bar{y_1}-\bar{y_2})\). Esto quiere decir que el \(a\) máximo no es único, pero la dirección sí.\
La dirección óptima dada por \(S_{pl}^{-1}(\bar{y_1}-\bar{y_2})\) es paralelamente eficiente a la línea que une \(\bar{y_1}\) y \(\bar{y_2}\) porque \[\frac{(\bar{z_1}-\bar{z_2})^2}{s_z^2} = (\bar{y_1}-\bar{y_2})'S_{pl}(\bar{y_1}-\bar{y_2})\]
para \(z=a'y\) con \(a=S_{pl}^{-1}(\bar{y_1}-\bar{y_2})\)
Las combinaciones lineales \(z_{1i}=a'y_{1i}=a_1y_{1i1}+a_2y{1i2}\) y \(z_{2i}=a'y_{2i}=a_1y_{2i1}+a_2y_{2i2}\) proyecta los puntos \(y_{1i},y_{2i}\) en la línea que maximiza la separación de los dos grupos. Dado que las variables \(y_1,y_2\) son normal bivariadas, una combinación lineal \(z=a_1y_1+a_2y_2=a'y\) es normal univariada.
Función discriminante para múltiples (k) grupos
Se desea encontrar la combinación lineal de variables que mejor separe los \(k\) grupos de múltiples observaciones.
Sea \(n_i\) el número de observaciones para el \(i\)-ésimo grupo. Cada vector de observación \(y_{ij}\) se transforma para obtener un \(z_{ij}=a'y_{ij};i=1,...,k;j=1,...,n_i\) y se hallan las medias \(\bar{z_i}=a'\bar{y_i}\). Como para el caso de dos grupos, se busca e vector \(a\) que maximice la separación entre \(\bar{z_1},\bar{z_2},...,\bar{z_k}\).
Retomando la ecuación, se tiene \[\frac{(\bar{z_1}-\bar{z_2})^2}{s_z^2} = \frac{[a'(\bar{y_1}-\bar{y_2})]^2}{a'S_{pl}a}=\frac{a'(\bar{y_1}-\bar{y_2})(\bar{y_1}-\bar{y_2})'a}{a'S_{pl}a}\]
Ahora, para extender lo anterior a \(k\) grupos, se usan las matrices de MANOVA \(H\) en lugar de \((\bar{y_1}-\bar{y_2})(\bar{y_1}-\bar{y_2})'\) y \(E\) en lugar de \(S_{pl}\), teniendo \[
\lambda = \frac{a'Ha}{a'Ea}\] \[
\lambda =\frac{SSH(z)}{SSE(z)} \] donde \(SSH(z)\) y \(SSE(z)\) son las sumas entre y al interior de los cuadrados de \(z\)
La ecuación anterior se puede reescribir como \[ a'Ha=\lambda a'Ea \] \[ a'(Ha-\lambda Ea)=0 \]
Se ve claramente que existen dos posibles soluciones del sistema, de las cuales \(a'=0'\) se descarta porque produce \(\lambda=0/0\). La otra posibilidad es
\[Ha-\lambda Ea = 0\]
\[(E^{-1}H-\lambda I)a=0\]
cuya solución son los valores propios \(\lambda_1,\lambda_2,...,\lambda_s\) y sus vectores propios asociados \(a_1,a_2,...,a_s\) de \(E^{-1}H\). El número \(s\) de valores propios (‘non-zeros’) es el rango de la matriz H, que se puede encontrar como el \(min\lbrace k-1,p\rbrace\). Los \(\lambda\)s se encuentran ordenados en forma descendente, siendo \(z_1=a'_1y\) la función discriminante que maximalmente separa las medias.
De los vectores propios \(a_1,a_2,...,a_s\) se obtienen \(s\) funciones discriminantes \(z_1=a'_1,z_2=a'_2,...,z_s=a'_s\), que muestran las dimensiones o direcciones en las diferencias entre \(\bar{y_1},\bar{y_2},...,\bar{y_k}\). Estas funciones no están correlacionadas ni son ortogonales porque \(E^{-1}H\) no es simétrica.
Funciones discriminantes estandarizadas
Si las \(y\)s no son conmensurables, es decir, no poseen la misma escala y sus varianzas son incomparables, con grandes diferencias entre sí; se requieren coeficientes \(a^*_r\) que se apliquen a variables estandarizadas.
Considere el caso para dos grupos. Para el \(i\)-ésimo vector de observación \(y_{1i}\) o \(y_{2i}\) en el grupo 1 o 2, la función discriminante en términos de las variables estandarizadas sería
\[
z_{1i}=a^*_1\frac{y_{1i1}-\bar{y_{11}}}{s_1}+a^*_2\frac{y_{1i2}-\bar{y_{12}}}{s_2}+...+a^*_p\frac{y_{1ip}-\bar{y_{1p}}}{s_p} \qquad i=1,2,...,n_1\] \[
z_{1i}=a^*_1\frac{y_{2i1}-\bar{y_{21}}}{s_1}+a^*_2\frac{y_{2i2}-\bar{y_{22}}}{s_2}+...+a^*_p\frac{y_{2ip}-\bar{y_{2p}}}{s_p} \qquad i=1,2,...,n_2 \]
donde \(\bar{y'_1}=(\bar{y_{11}},\bar{y_{12}},...,\bar{y_{1p}})\) y \(\bar{y'_2}=(\bar{y_{21}},\bar{y_{22}},...,\bar{y_{2p}})\) son los vectores de medias para los dos grupos, y \(s_r\) es la desviación estándar entre muestra de la \(r\)-ésima variable que se obtiene de la raíz del \(r\)-ésimo elemento de la diagonal de \(S_{pl}\).
Claramente los coeficientes estandarizados son de la forma
\[a^*_r=s_ra_r \qquad r=1,2,...,p \] y vectorialmente como \[
a^*=(diag(S_{pl}))^{1/2}a
\]
Ahora considere el caso para múltiples grupos. Si se denota al \(r\)-ésimo coeficiente de la \(m\)-ésima función discriminante como \(a^*_{mr}\), se tiene \[a^*_{mr}=s_ra_{mr} \qquad m=1,2,...,s;r=1,2,...,p\] donde \(s_r\) se obtiene de \(S_{pl}=\frac{E}{v_{E}}\).\
Dado que el \(m\)-ésimo vector propio es único hasta la multiplicación por un escalar, la expresión anterior se puede simplificar como \[a^*_{mr}=\sqrt[]{e_{rr}}a_{mr}\] donde \(e_{rr}\) es el \(r\)-ésimo elemento de la diagonal de \(E\).
Importancia relativa
La importancia relativa de cada función discriminante está dada por \[\frac{\lambda_i}{\sum_{j=1}^{s}\lambda_j}\] Al igual que en Componentes Principales, dos o tres funciones discriminantes generalmente ofrecen suficiente información para explicar las diferencias entre los grupos (se busca una proporción acumulada de 0.8).
Tests de significancia
A diferencia al proceso del cálculo de la función discriminante, para los test de significancia se requiere del supuesto de normalidad multivariada.
Caso de dos grupos
Anteriormente, se vio que la separación de las medias de las variables transformadas obtenida por la función discriminante \(z=a'y\) es equivalente a la distancia estandarizada de los vectores de medias \(\bar{y_1},\bar{y_2}\). Esta distancia estandarizada es proporcional a la \(T^2\) para dos grupos \[T^2=\frac{n_1n_2}{n_1+n_2} (\bar{y_1}-\bar{y_2})'S^(-1)_pl(\bar{y_1}-\bar{y_2})\] Así, el vector de coeficientes \(a\) de la función discriminante es significativamente diferente de \(0\) si \(T^2\) es significativa.
\[H_0 : \alpha=0 \equiv H_0 :\mu_1=\mu_2 \qquad \text{donde } \alpha = \Sigma^{-1}(\mu_1-\mu_2)\]
Para probar la significancia de un subconjunto de coeficientes de la función discriminante, se puede usar el test de análisis del perfil para el correspondiente subconjunto de las \(y\)s.
Caso de múltiples grupos
Se denotó que el criterio discriminante \(\lambda=a'Ha/a'Ea\) es maximizado por \(\lambda_1\), el mayor valor propio de \(E^{-1}H\), y los restantes valores propios \(\lambda_2,...,\lambda_s\) correspondientes a las otras dimensiones discriminantes. Estos valores propios son los mismos que los del test \(\Lambda\) de Wilks para diferencias significativas entre de los vectores de medias \[
\Lambda_1=\prod_{i=1}^{s}\frac{1}{1+\lambda_i} \sim \Lambda_{p,k-1,N-k}
\] donde \(N=\sum_i n_i\). Dado que \(\Lambda_1\) es pequeño si uno o más \(\lambda_i\)s son grandes, \(\Lambda\) de Wilks prueba la significancia de los valores propios y de este modo de las funciones discriminantes. Los \(s\) valores propios representan las \(s\) dimensiones de separación de los vectores de medias \(\bar{y_1},\bar{y_2},...,\bar{y_k}\).
También se puede utilizar una aproximación \(\chi^2\) para \(\Lambda_1\) dada por \[
V_1=-[v_E-\frac{1}{2}(p-v_H+1)]\log\Lambda_1\] \[
=-[N-1-\frac{1}{2}(p+k)]\log\prod_{i=1}^s\frac{1}{1+\lambda_i}\] \[
=[N-1-\frac{1}{2}(p+k)]\sum_{i=1}^s \log(1+\lambda_i) \sim \chi^2_{p(k-1)}
\]
El test estadístico miden la significancia de \(\lambda_1,\lambda_2,...,\lambda_s\). Si se rechaza \(H_0\), se concluye que al menos un \(\lambda\) es significativamente diferente de cero, y hay al menos una dimensión de separación de los vectores de medias. Como \(\lambda_1\) es el mayor, solo se está seguro de su significancia, junto con la de \(z_1=a'_1y\).
Dado que se está interesado en saber cuáles de estas dimensiones son significativas, se hace el test \(\Lambda\) de Wilkins iterativamente hasta que \(H_0\) sea aceptada. Las dimensiones significativas resultantes serían las asociadas a \(\lambda_1,...,\lambda_{r-1}\), siendo \(r\) la etapa de aceptación de \(H_0\).
El estadístico en la etapa \(m\), es decir para probar la significancia de \(\lambda_m,...,\lambda_s\), es \[
\Lambda_m=\prod_{i=m}^{s}\frac{1}{1+\lambda_i} \sim \Lambda_{p-m+1,k-m,N-k-m+1}
\] y su aproximación \(\chi^2\) es \[
V_m=-[N-1-\frac{1}{2}(p+k)]\log\Lambda_m \] \[
=[N-1-\frac{1}{2}(p+k)]\sum_{i=m}^s \log(1+\lambda_i) \sim \chi^2_{(p-m+1)(k-m)}
\]
En muchos casos, este test arroja más \(\lambda\)s significativos que los que se pueden considerar de importancia práctica. Si \(\lambda_i/\sum_j\lambda_j\) es pequeño, la función discriminante asociada podría no ser de interés, a pesar de ser significante.\
También se puede utilizar una aproximación \(F\). Para \(\Lambda_m\), se utiliza el estadístico \[
F=\frac{1-\Lambda_m^{1/t}}{\Lambda_m^{1/t}}
\] donde \[
t = \sqrt[]{\frac{(p-m+1)^2(k-m)^2-4}{(p-m+1)^2(k-m)^2-5}}\] \[
w = N-1-\frac{1}{2}(p+k)\] \[
df_1 = (p-m+1)(k-m)\] \[
df_2 = wt-\frac{1}{2}[(p-m+1)(k-m)-2]
\]
Interpretación de funciones discriminantes
Existe una alta correspondencia entre la interpretación de funciones discriminantes y la determinación de la contribución de cada variable. En la interpretación, los signos de los coeficientes se tienen en cuenta; mientras que en determinar la contribución, los signos son ignorados y se rankean los coeficientes en valor absoluto. Generalmente, el interés recae en el proceso de contribución. A continuación se presentan tres métodos diferentes para determinar la contribución de cada variable en la separación de los grupos.
Método 1: Coeficientes estandarizados
Para compensar la diferencia de escala entre las variables, los coeficientes pueden ser estandarizados usando las ecuaciones, donde los coeficientes son ajustados y aplican a variables estandarizadas. Como las variables estandarizadas \((y_kip-\bar{y_kp})/s_p\) son libres de escala, los coeficientes estandarizados reflejan correctamente la contribución conjunta de las variables a la función discriminante \(z\) que maximiza la separación de los grupos. El valor absoluto de los coeficientes puede ser usado para rankear las variables ne orden a su contribución a la diferenciación de los grupos. Para profundizar, se puede interpretar las funciones discriminantes teniendo en cuenta los signos de los coeficientes.
La función discriminante, al igual que otras combinaciones lineales, está sujeta a ciertas limitaciones como que los coeficientes de una variable pueden cambiar radicalmente si una variable es adicionada o removida y que estos pueden no ser estables si el tamaño de la muestra no es relativamente grande frente al número de variables. Si el \(N/p\) es muy pequeño, las variables que rankean alto en una muestra, lo pueden no ser en otra.
Método 2: Valores \(F\)-parciales
Para cualquier variable \(y_r\), se puede calcular un test \(F\)-parcial mostrando la importancia de \(y_r\) después de ajustar a las otras variables, en otras palabras, la separación dada por \(y_r\) en contraposición a aquella dada por las otras variables.\
Para el caso de dos grupos, la \(F\)-parcial está dada por \[F=(v-p+1)\frac{T^2_p-T^2_{p-1}}{v+T^2_{p-1}} \sim F_{1,v-p+1}\] donde \(T^2_p\) es \(T^2\) de Hotelling para dos muestras con todas las \(p\) variables, \(T^2_{p-1}\) es un estadístico \(T^2\) con todas las variables exceptuando \(y_r\), y \(v=n_1+n_2-2\).\
Para el caso de múltiples grupos, el \(\Lambda\)-parcial para \(y_r\) ajustados para las otras \(p-1\) variables está dado por \[\Lambda(y_r|y_1,..,y_{r-1},y{r+1},...,y_p) = \frac{\Lambda_p}{\Lambda_{p-1}} \sim \Lambda_{1,v_H,v_E-p+1}\] donde \(\Lambda_p\) es un \(\Lambda\) de Wilkins para todas las \(p\) variables y \(\Lambda_{p-1}\) contiene a todas las variables exceptuando a \(y_r\). El correspondiente \(F\) es \[F=\frac{1-\Lambda}{\Lambda}\frac{v_E-p+1}{v_H} \sim F_{v_H,v_E-p+1}\] donde \(\Lambda\) es definido en la ecuación anterior, \(v_E=N-k\) y \(v_H=k-1\).\
Los valores \(F\)-parciales tanto para el caso de dos grupos como para el caso de múltiples grupos no son asociados a una simple dimensión de la separación de grupos, como los son los coeficientes de la función discriminante estandarizada. Ejemplificando lo anterior, \(y_r\) tendrá una contribución diferente para cada una de las \(s\) funciones discriminantes, pero el \(F\)-parcial para esta variable constituye un índice global de contribución de ella en la separación de los grupos, a través de todas las dimensiones. A pesar de lo anterior, generalmente los valores de \(F\)-parciales clasifican las variables en el mismo orden que lo hacen los coeficientes estandarizados de la primera función discriminante, más aún si la proporción relativa de \(\lambda_1\) es grande.\
Un índice parcial de asociación para \(y_r\) puede ser definido por \[R^2=1-\Lambda_r \qquad r=1,2,...,p\] donde \(\Lambda_r\) es \(\Lambda\)-parcial para \(y_r\). \(R^2\) parcial es una medida de asociación entre las variables de agrupamiento y \(y_i\) después de ajustar las otras \((p-1)\) \(y\)s.
Otros métodos: Correlación entre las variables y las funciones discriminantes, y Rotación
menciona que muchos libros e investigaciones destacan a la correlación entre las variables y las funciones discriminantes, \(r_{y_i,z_j}\), como la mejor manera de medir la importancia de las variables. Se dice que estás correlaciones son más informativas con respecto a la contribución conjunta de las variables a las funciones discriminantes que los coeficientes estandarizados. Sin embargo, cita a Rencher quién ha mostrado que las correlaciones en cuestión muestran la contribución de cada variable en un contexto univariado mejor que en un multivariado. En consecuencia, solo muestra cómo cada variable en sí misma separa los grupos, ignorando la presencia de otras variables; es así como no muestran la contribución conjunta a la separación de los grupos. Este método es engañoso en la interpretación de la función discriminante.\
La rotación de los coeficientes de la función discriminante es recomendable a veces. Este procedimiento busca conseguir un patrón con los coeficientes en valor absoluto cercanos a 0 o a 1. Aunque las funciones discriminantes quedan más fáciles de interpretar, se presentan dos problemas y es que los coeficientes no maximizan la separación de los grupos y son correlacionados.\
recomienda para la interpretación de las funciones discriminantes el uso de los coeficientes estandarizados antes que el de correlaciones o el de rotación.
Gráfica de dispersión
Una de las ventajas del Análisis Discriminante es la reducción de la dimensionalidad. Usualmente, las primeras dos o tres funciones discriminantes poseen la mayor maximización de los grupos, haciendo posible la realización de un gráfico de dispersión en 2D. Ahora, si la dimensionalidad esencial es mayor a dos, se verá posiblemente un superposición entre grupos.\
Al hacer la gráfica se deben incluir tanto los valores para las observaciones \(y_{ij}\) como \[z_{ij}=\left(z_{1ij} \quad z_{2ij}\right)'= \left(a_1'y_{ij} \quad a_2'y_{ij}\right)'=Ay_{ij} \qquad i=1,...,k;j=1,...,n_i\] y los vectores de medias transformadas \[\bar{z_{i}}=\left(\bar{z_{1i}} \quad \bar{z_{2i}}\right)'=A\bar{y_{i}} \qquad i=1,...,k\]
Implementaciones
Este trabajo utiliza la famosa base de datos “iris”, esta contiene mediciones de 150 flores iris de tres especies diferentes.
Las tres clases del conjunto de datos Iris:
Iris-setosa (n = 50) Iris-versicolor (n = 50) Iris-virginica (n = 50) Las cuatro características del conjunto de datos Iris:
Longitud del sepal en cm Ancho sepal en cm Longitud del pétalo en cm Anchura del pétalo en cm
library(ggplot2)
library(grid)
library(gridExtra)
head(iris)
Para obtener una idea aproximada de cómo se distribuyen las muestras de las tres clases, se visualizará las distribuciones de las cuatro características diferentes en histogramas unidimensionales.
dat <- data.frame(iris$Sepal.Length, iris$Species)
SL <-ggplot(dat,aes(x=iris.Sepal.Length)) +
geom_histogram(data=subset(dat,iris.Species == "setosa"),fill = "red", alpha = 0.4, bins = 50) +
geom_histogram(data=subset(dat,iris.Species == "versicolor"),fill = "blue", alpha = 0.4, bins = 50) +
geom_histogram(data=subset(dat,iris.Species == "virginica"),fill = "green", alpha = 0.4, bins = 50)
dat <- data.frame(iris$Sepal.Width, iris$Species)
SW <-ggplot(dat,aes(x=iris.Sepal.Width)) +
geom_histogram(data=subset(dat,iris.Species == "setosa"),fill = "red", alpha = 0.2, bins = 50) +
geom_histogram(data=subset(dat,iris.Species == "versicolor"),fill = "blue", alpha = 0.2, bins = 50) +
geom_histogram(data=subset(dat,iris.Species == "virginica"),fill = "green", alpha = 0.2, bins = 50)
dat <- data.frame(iris$Petal.Length, iris$Species)
PL <-ggplot(dat,aes(x=iris.Petal.Length)) +
geom_histogram(data=subset(dat,iris.Species == "setosa"),fill = "red", alpha = 0.4, bins = 50) +
geom_histogram(data=subset(dat,iris.Species == "versicolor"),fill = "blue", alpha = 0.4, bins = 50) +
geom_histogram(data=subset(dat,iris.Species == "virginica"),fill = "green", alpha = 0.4, bins = 50)
dat <- data.frame(iris$Petal.Width, iris$Species)
PW <-ggplot(dat,aes(x=iris.Petal.Width)) +
geom_histogram(data=subset(dat,iris.Species == "setosa"),fill = "red", alpha = 0.4, bins = 50) +
geom_histogram(data=subset(dat,iris.Species == "versicolor"),fill = "blue", alpha = 0.4, bins = 50) +
geom_histogram(data=subset(dat,iris.Species == "virginica"),fill = "green", alpha = 0.4, bins = 50)
# Setosa -> Red
# Versicolor -> Blue
# Virginica -> Green
grid.arrange(SL, SW,PL, PW, ncol = 2)

Solo al mirar el histograma es facil detectar que las caracteristicas asociados a los petalos son mas utiles al momento de distanciar las caracteristica.
LDA via MASS
library(MASS)
r <- lda(formula = Species ~ .,
data = iris,
prior = c(1,1,1)/3)
r$prior
setosa versicolor virginica
0.3333333 0.3333333 0.3333333
r$counts
setosa versicolor virginica
50 50 50
r$means
Sepal.Length Sepal.Width Petal.Length Petal.Width
setosa 5.006 3.428 1.462 0.246
versicolor 5.936 2.770 4.260 1.326
virginica 6.588 2.974 5.552 2.026
r$scaling
LD1 LD2
Sepal.Length 0.8293776 0.02410215
Sepal.Width 1.5344731 2.16452123
Petal.Length -2.2012117 -0.93192121
Petal.Width -2.8104603 2.83918785
r$svd
[1] 48.642644 4.579983
prop <- r$svd^2/sum(r$svd^2)
prop
[1] 0.991212605 0.008787395
r2 <- lda(formula = Species ~ .,
data = iris,
prior = c(1,1,1)/3,
CV = TRUE)
head(r2$class)
[1] setosa setosa setosa setosa setosa setosa
Levels: setosa versicolor virginica
head(r2$posterior, 3)
setosa versicolor virginica
1 1 5.087494e-22 4.385241e-42
2 1 9.588256e-18 8.888069e-37
3 1 1.983745e-19 8.606982e-39
train <- sample(1:150, 75)
r3 <- lda(Species ~ ., # training model
iris,
prior = c(1,1,1)/3,
subset = train)
plda = predict(object = r, # predictions
newdata = iris[-train, ])
head(plda$class) # classification result
[1] setosa setosa setosa setosa setosa setosa
Levels: setosa versicolor virginica
head(plda$posterior, 3) # posterior prob.
setosa versicolor virginica
2 1 7.217970e-18 5.042143e-37
6 1 3.883282e-21 4.566540e-40
9 1 1.902813e-15 9.482936e-34
head(plda$x, 3) # LD projection
LD1 LD2
2 7.128688 -0.7866604
6 7.701947 1.4617210
9 6.560552 -1.0151636
library(MASS)
library(ggplot2)
library(scales)
library(gridExtra)
pca <- prcomp(iris[,-5],
center = TRUE,
scale. = TRUE)
prop.pca = pca$sdev^2/sum(pca$sdev^2)
lda <- lda(Species ~ .,
iris,
prior = c(1,1,1)/3)
prop.lda = r$svd^2/sum(r$svd^2)
plda <- predict(object = lda,
newdata = iris)
dataset = data.frame(species = iris[,"Species"],
pca = pca$x, lda = plda$x)
p1 <- ggplot(dataset) + geom_point(aes(lda.LD1, lda.LD2, colour = species, shape = species), size = 2.5) +
labs(x = paste("LD1 (", percent(prop.lda[1]), ")", sep=""),
y = paste("LD2 (", percent(prop.lda[2]), ")", sep=""))
p2 <- ggplot(dataset) + geom_point(aes(pca.PC1, pca.PC2, colour = species, shape = species), size = 2.5) +
labs(x = paste("PC1 (", percent(prop.pca[1]), ")", sep=""),
y = paste("PC2 (", percent(prop.pca[2]), ")", sep=""))
grid.arrange(p1, p2)

LDA for Classification
library(MASS)
X <- matrix(c(2,3,3,4,4,5,5,6,5,7,2,1,3,2,4,2,4,3,6,4,7,6,1,6,2,7,1,7), ncol=2, byrow=T)
Y <- c(1,1,1,1,1,2,2,2,2,2,2,3,3,3);
plot(X,col=Y, pch=19)

idx_class1 <- which(Y==1) # indexes of the observations for each class
idx_class2 <- which(Y==2)
my.data <- data.frame(X1=X[,1], X2=X[,2], Y=Y)
model <- lda(Y ~ . , data=my.data)
model
Call:
lda(Y ~ ., data = my.data)
Prior probabilities of groups:
1 2 3
0.3571429 0.4285714 0.2142857
Group means:
X1 X2
1 3.800000 5.000000
2 4.333333 3.000000
3 1.333333 6.666667
Coefficients of linear discriminants:
LD1 LD2
X1 -2.025094 0.3695336
X2 1.963192 0.2946290
Proportion of trace:
LD1 LD2
0.9985 0.0015
projected.data <- X %*% model$scaling
plot(projected.data, col=Y, pch=19)

# now let's predict the class of a new sample
new.data <- data.frame(X1=c(2), X2=c(5.1)) # new data point should be class 3
prediction <- predict(model, new.data)
model$prior # the prior probabilities before the new data point
1 2 3
0.3571429 0.4285714 0.2142857
prediction$posterior # the posterior probs for the classes given this new data point
1 2 3
1 0.9397416 2.147726e-14 0.06025842
plot(X,col=Y, pch=19)
# Let's plot it with the color for the class it predicts:
points(new.data$X1, new.data$X2, pch=17, col=prediction$class)
# Plot the means:
points(model$means, pch="+", col=1:3)

library(klaR) # Classification and visualization package
partimat(x=my.data[,-3], grouping=as.factor(my.data[,3]), method="lda",
col.mean=1, image.colors = c("lightgrey","red","green"))

model <- qda(Y ~ . , data=my.data)
new.data <- data.frame(X1=c(2), X2=c(5.1)) # new data point should be class 3
prediction <- predict(model, new.data)
prediction$posterior
1 2 3
1 5.403558e-05 8.261899e-14 0.999946
partimat(x=my.data[,-3], grouping=as.factor(my.data[,3]), method="qda",
col.mean=1, image.colors = c("lightgrey","red","green"))

LDA for Dimensionality Reduction before Classification
set.seed(101)
# Read Sample Data
my.data <- read.csv(url("http://archive.ics.uci.edu/ml/machine-learning-databases/wine/wine.data"),
header=FALSE)
unable to resolve 'archive.ics.uci.edu'Error in open.connection(file, "rt") : no se puede abrir la conexión
LDA projections
library(MASS)
Warning message:
cerrando la conenexion 3 (http://archive.ics.uci.edu/ml/machine-learning-databases/wine/wine.data) que no esta siendo utilizada
X <- scale(as.matrix(iris[,-5])) # better scale mean and variance before LDA
Y <- unclass(iris$Species)
iris1 <- data.frame(X=X, Y=Y)
colnames(iris1) <- colnames(iris)
head(iris1)
model <- lda(Species ~ . , data=iris1, prior=c(1,1,1)/3)
plot(iris1[,"Sepal.Length"], iris1[,"Petal.Length"],
col=c("blue","green","red")[iris1$Species], pch=19,
xlab="Sepal Length", ylab="Petal.Length")
means <- model$means
points(means[,c(1,3)], pch=3, lwd=2, col="purple")

model
Call:
lda(Species ~ ., data = iris1, prior = c(1, 1, 1)/3)
Prior probabilities of groups:
1 2 3
0.3333333 0.3333333 0.3333333
Group means:
Sepal.Length Sepal.Width Petal.Length Petal.Width
1 -1.0111914 0.8504137 -1.3006301 -1.2507035
2 0.1119073 -0.6592236 0.2843712 0.1661774
3 0.8992841 -0.1911901 1.0162589 1.0845261
Coefficients of linear discriminants:
LD1 LD2
Sepal.Length 0.6867795 0.01995817
Sepal.Width 0.6688251 0.94344183
Petal.Length -3.8857950 -1.64511887
Petal.Width -2.1422387 2.16413593
Proportion of trace:
LD1 LD2
0.9912 0.0088
model$scaling # the parameters of the linear discriminant functions
LD1 LD2
Sepal.Length 0.6867795 0.01995817
Sepal.Width 0.6688251 0.94344183
Petal.Length -3.8857950 -1.64511887
Petal.Width -2.1422387 2.16413593
pred <- predict(model, iris[,-5])
# The next horizontal axis are meaningless, they depends on the sample order of the dataset
head(pred$x[,1]) # contains the values of the dataset observations for the first discriminant function
1 2 3 4 5 6
-0.02509743 -0.49686587 -0.11187726 -1.02459673 -0.02689287 -1.14571979
plot(pred$x[,1], col=c("blue","green","red")[iris1$Species], pch=19) # we can plot them

# Notice that the 2nd discriminant function does not separate that well the 2nd & 3rd class
plot(pred$x[,2], col=c("blue","green","red")[iris1$Species], pch=19) # we can plot them

vec <- c(model$scaling[1,1], model$scaling[3,1])
v <- vec / sqrt(sum(vec^2)) # make it a unit vector
lda1.points <- as.matrix(iris1[,c(1,3)]) %*% v %*% t(v) # to project point X into unit vector v just calculate X.v.v^T
plot(iris1[,"Sepal.Length"], iris1[,"Petal.Length"],
col=c("blue","green","red")[iris1$Species], pch=19,
xlab="Sepal Length", ylab="Petal.Length", , main="1st discriminant functions")
segments(-vec[1],-vec[2],vec[1],vec[2])
# points(lda1.points , col=c("blue","green","red")[iris1$Species], pch=18) # draw projection point
for(i in 1:nrow(iris1)) {
segments(iris1[i,1], iris1[i,3], lda1.points[i,1], lda1.points[i,2],
lty=2, col=c("blue","green","red")[iris1[i,]$Species])
}

vec <- c(model$scaling[1,2], model$scaling[3,2])
v <- vec / sqrt(sum(vec^2))
lda2.points <- as.matrix(iris1[,c(1,3)]) %*% v %*% t(v)
plot(iris1[,"Sepal.Length"], iris1[,"Petal.Length"],
col=c("blue","green","red")[iris1$Species], pch=19,
xlab="Sepal Length", ylab="Petal.Length", , main="2nd discriminant functions")
segments(-2*vec[1],-2*vec[2],2*vec[1],2*vec[2])
# points(lda2.points , col=c("blue","green","red")[iris1$Species], pch=18) # draw projection point
for(i in 1:nrow(iris1)) {
segments(iris1[i,1], iris1[i,3], lda2.points[i,1], lda2.points[i,2],
lty=2, col=c("blue","green","red")[iris1[i,]$Species])
}

Cangrejos
lcrabs <- log(crabs[,4:8])
dcrabs.lda <- lda(crabs$sex~ FL + RW + CL + CW, lcrabs)
dcrabs.lda$scaling
LD1
FL -2.889616
RW -25.517644
CL 36.316854
CW -11.827981
table(crabs$sex, predict(dcrabs.lda)$class)
F M
F 97 3
M 3 97
crabs.grp <- c("B", "b", "O", "o")[rep(1:4, each = 50)]
dcrabs.lda4 <- lda(crabs.grp~ FL + RW + CL + CW, lcrabs)
dcrabs.lda4
Call:
lda(crabs.grp ~ FL + RW + CL + CW, data = lcrabs)
Prior probabilities of groups:
b B o O
0.25 0.25 0.25 0.25
Group means:
FL RW CL CW
b 2.564985 2.475174 3.312685 3.462327
B 2.672724 2.443774 3.437968 3.578077
o 2.852455 2.683831 3.529370 3.649555
O 2.787885 2.489921 3.490431 3.589426
Coefficients of linear discriminants:
LD1 LD2 LD3
FL 36.25600 -4.844633 -19.10647
RW 13.38368 22.786954 7.07711
CL 20.28868 -48.380432 58.34517
CW -65.64476 33.710217 -49.51270
Proportion of trace:
LD1 LD2 LD3
0.6422 0.3491 0.0087
dcrabs.pr4 <- predict(dcrabs.lda4, dimen = 2)
dcrabs.pr2 <- dcrabs.pr4$post[, c("B", "O")] %*% c(1, 1)
table(crabs$sex, dcrabs.pr2 > 0.5)
FALSE TRUE
F 96 4
M 3 97
cr.t <- dcrabs.pr4$x[, 1:2]
eqscplot(cr.t, type = "n", xlab = "First LD", ylab = "Second LD")
text(cr.t, labels = as.character(crabs.grp))
perp <- function(x, y) {
m <- (x+y)/2
s <- - (x[1] - y[1])/(x[2] - y[2])
abline(c(m[2] - s*m[1], s))
invisible()
}
cr.m <- lda(cr.t, crabs$sex)$means
points(cr.m, pch = 3, mkh = 0.3)
perp(cr.m[1, ], cr.m[2, ])
cr.lda <- lda(cr.t, crabs.grp)
x <- seq(-6, 6, 0.25)
y <- seq(-2, 2, 0.25)
Xcon <- matrix(c(rep(x,length(y)), rep(y, each = length(x))),,2)
cr.pr <- predict(cr.lda, Xcon)$post[, c("B", "O")] %*% c(1,1)
contour(x, y, matrix(cr.pr, length(x), length(y)),
levels = 0.5, labex = 0, add = T, lty= 3)

LS0tCnRpdGxlOiAiQW5hbGlzaXMgZGlzY3JpbWluYW50ZSIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQojIyMjIFBvcjogU2FudGlhZ28gSGluY2FwaWUgeSBBbmRyZWEgUG9zYWRhCgojIEludHJvZHVjY2lvbgpFbCBBbsOhbGlzaXMgRGlzY3JpbWluYW50ZSwgdGFtYmnDqW4gY29ub2NpZG8gY29tbyBBbsOhbGlzaXMgZGUgbGEgZnVuY2nDs24gZGlzY3JpbWluYW50ZSwgZXMgdW5hIHTDqWNuaWNhIGRlc2NyaXB0aXZhCgoqIERlc2NyaWJpciBjYXJhY3RlcsSxzIFzdGljYXMgZXNwZWPEscyBZmljYXMgcGFyYSBsYSBkaXN0aW5jacOzbiBkZSBsb3MgZ3J1cG9zLCBwb3IgbWVkaW8gZGUgZnVuY2lvbmVzIGxpbmVhbGVzIGNvbm9jaWRhcyBjb21vIGZ1bmNpb25lcyBkaXNjcmltaW5hbnRlcy4gKEFuw6FsaXNpcyBkaXNjcmltaW5hbnRlIGRlc2NyaXB0aXZvKQoqIENsYXNpZmljYXIgY2Fzb3MgLWluZGl2aWR1b3MtIGVuIGdydXBvcyBwcmVleGlzdGVudGVzIHNlZ8O6biBsYXMgc2ltaWxpdHVkZXMgZW50cmUgZWwgY2FzbyB5IGxvcyBjYXNvcyBwZXJ0ZW5lY2llbnRlcyBsb3MgZ3J1cG9zLCBtZWRpYW50ZSBmdW5jaW9uZXMgbGluZWFsZXMgbyBjdWFkcsOhdGljYXMgcXVlIHJlY2liZW4gZWwgbm9tYnJlIGRlIGZ1bmNpb25lcyBjbGFzaWZpY2Fkb3JhcyAoQW7DoWxpc2lzIGRpc2NyaW1pbmFudGVzIHByZWRpY3Rpdm8pCgpFbCBhbsOhbGlzaXMgZGVzY3JpcHRpdm8gaW5jbHV5ZSBpZGVudGlmaWNhciBsYSBjb250cmlidWNpw7NuIHJlbGF0aXZhIGRlICRwJCB2YXJpYWJsZXMgYSBsYSBzZXBhcmFjacOzbiBkZSBsb3MgZ3J1cG9zIHkgZW5jb250cmFyIHVuIHBsYW5vIMOzcHRpbW8gZG9uZGUgbG9zIHB1bnRvcyBwdWVkYW4gc2VyIHByb3llY3RhZG9zIGlsdXN0cmFuZG8gZGUgbGEgbWVqb3IgbWFuZXJhIGxhIHNlcGFyYWNpw7NuIGRlIGxvcyBncnVwb3MuIAoqIMK/ZGUgcXXDqSBtYW5lcmEgZGlmaWVyZW4gbG9zIGdydXBvcyBlbiBlc3R1ZGlvPwoqIMK/cXXDqSBkaWZlcmVuY2lhcyBleGlzdGVuIGVudHJlIHkgZW4gbWVkaW8gZGUgbG9zIGdydXBvcyBwYXJhIHVuIGNvbmp1bnRvIGVzcGVjxLHMgWZpY28gZGUgdmFyaWFibGVzPwoqIMK/Y3XDoWxlcyB2YXJpYWJsZXMgY29udGludWFzIGNhcmFjdGVyaXphbiBtZWpvciBhIGNhZGEgZ3J1cG8sIG8gY3XDoWxlcyBubyBzb24gY2FyYWN0ZXLEscyBc3RpY2FzIGRlIGNhZGEgdW5vPyAKClBvciBvdHJvIGxhZG8sIGVsIGFuw6FsaXNpcyBwcmVkaWN0aXZvIHJlc3BvbmRlIGE6IAoqIGRhZGFzIGxhcyBjYXJhY3RlcsSxzIFzdGljYXMgZGUgdW4gY2FzbyBjb21vIG1lZGlibGVzIHBvciB1biBjb25qdW50byBkZSB2YXJpYWJsZSDCv2EgcXXDqSBncnVwbyBwcmVkZWZpbmlkbyBwZXJ0ZW5lY2UgZGljaG8gY2Fzbz8KCiMgQW7DoWxpc2lzIERpc2NyaW1pbmFudGUgRGVzY3JpcHRpdm8KCkVsIHNpZ3VpZW50ZSBlc3F1ZW1hIHNpcnZlIGRlIGd1xLHMgWEgcGFyYSBsbGV2YXIgYSBjYWJvIGVsIGFuw6FsaXNpcyBkZXNjcmlwdGl2bwoqIERldGVybWluYXIgc2kgZWwgQW7DoWxpc2lzIERpc2NyaW1pbmFudGUgcHJvcG9yY2lvbmEgcmVzdWx0YWRvcyBlc3RhZMSxzIFzdGljb3MsIGVzIGVsIHTDqWNuaWNhIGFkZWN1YWRhLCBwYXJhIGxhIHNvbHVjacOzbiBkZSBzdSBwcm9ibGVtYS4KKiBEZWZpbmlyIGxvcyBncnVwb3MgeSBzZWxlY2Npb25hciBsYXMgdmFyaWFibGVzIHF1ZSBzZSB1dGlsaXphbiBlbiBlbCBhbsOhbGlzaXMgKGdydXBvcyB5IHZhcmlhYmxlcyBkZSBpbnRlcsOpcykuCiogRGV0ZXJtaW5hciBzaSBsb3MgZGF0b3Mgc29uIGFwcm9waWFkb3M7IGVzIGRlY2lyLCBzaSBjdW1wbGVuIGxvcyBzdXB1ZXN0b3MuIEVuIGNhc28gZGUgbm8gY3VtcGxpciBhbGd1bm8gZGUgbG9zIHN1cHVlc3RvcywgZGV0ZXJtaW5hciBsYSBncmF2ZWRhZCBkZWwgaW5jdW1wbGltaWVudG8gZGUgbG9zIG1pc21vcywgcXVlIHRhbnRvIHNlIGFmZWN0YSBsYSByb2J1c3RleiBkZSBsb3MgcmVzdWx0YWRvcy4KKiBMbGV2YXIgYSBjYWJvIGVsIGFuw6FsaXNpcyB5IGludGVycHJldGFyIGxvcyByZXN1bHRhZG9zLgoKU2UgY29uc2lkZXJhIGRlIGltcG9ydGFuY2lhIG1lbmNpb25hciBsb3Mgb2JqZXRpdm9zIGVuIGxhIGRpc2NyaW1pbmFjacOzbiBkZSBtw7psdGlwbGVzIGdydXBvcywgcG9yIGxvIHF1ZSBzZSBwcmVzZW50YW4KYSBjb250aW51YWNpw7NuOgoqIEV4YW1pbmFyIGxhIHNlcGFyYWNpw7NuIGRlIGdydXBvcyBlbiB1bmEgZ3LDoWZpY2EgMkQuIEN1YW5kbyBoYXkgbcOhcyBkZSBkb3MgZ3J1cG9zLCBzZSByZXF1aWVyZSBtw6FzIGRlIHVuYSBmdW5jacOzbiBkaXNjcmltaW5hbnRlIHBhcmEgZGVzY3JpYmlyIGxhIHNlcGFyYWNpw7NuIGRlIGxvcyBncnVwb3MuCiogRW5jb250cmFyIHVuIHN1YmNvbmp1bnRvIGRlIGxhcyB2YXJpYWJsZXMgb3JpZ2luYWxlcyBxdWUgc2VwYXJlbiBsb3MgZ3J1cG9zIGFsIG1lbm9zIHRhbiBiaWVuIGNvbW8gw6lzdGFzLgoqIENsYXNpZmljYXIgbGFzIHZhcmlhYmxlcyBzZWfDum4gc3UgYXBvcnRlIHJlbGF0aXZvIGEgbGEgc2VwYXJhY2nDs24gZGUgbG9zIGdydXBvcy4gU2UgY29uc2lkZXJhIGNvbiBtYXlvciB2YWxpZGV6IHNpIHNlIHRyYWJhamEgbGEgZnVuY2nDs24gZGlzY3JpbWluYW50ZSBlc3RhbmRhcml6YWRhLgoqIEludGVycHJldGFyIGRlIGxhcyBkaW1lbnNpb25lcy4KCiMjIEFzcGVjdG9zIHByZXZpb3M6IEdydXBvcyB5IHZhcmlhYmxlcwpFbCBBbsOhbGlzaXMgRGlzY3JpbWluYW50ZSByZXF1aWVyZSBkZSB1biBjb25qdW50byBkZSBkYXRvcyBxdWUgY29udGVuZ2EgZG9zIG8gbcOhcyBncnVwb3MgbXV0dWFtZW50ZSBleGNsdXllbnRlcyB5IHB1bnR1YWNpb25lcyBlbiBkb3MgbyBtw6FzIHZhcmlhYmxlcyBwYXJhIGNhZGEgY2FzbyBlbiBlbCBncnVwby4KCkxvcyBncnVwb3MgZGViZW4gc2VyIGNvbnN0cnVpZG9zIGRlIHRhbCBmb3JtYSBxdWUgc2VhbiBsYXMgY2F0ZWdvcsSxzIFhcyBkZSBpbmRpdmlkdW9zIGVuIGxvcyBjdWFsZXMgcmVjYWUgZWwgaW50ZXLDqXMgZGlmZXJlbmNpYWNpw7NuLiAKClBvciBvdHJvIGxhZG8sIGxhcyB2YXJpYWJsZXMgZGViZW4gc2VyIGVzY29naWRhcyBjb21vIGFxdWVsbGFzIGVuIGxhcyBxdWUgc2UgY29uc2lkZXJhIG8gY3JlZSBxdWUgbG9zIGdydXBvcyB2YW4gYSBkaWZlcmlyIHBvdGVuY2lhbG1lbnRlLiAKCkVsIEFuw6FsaXNpcyBEaXNjcmltaW5hbnRlIHNlIGVuY2FyZ2Fyw6EgZGUgZXZhbHVhciBlbCBncmFkbyBlbiBxdWUgZGljaGFzIHZhcmlhYmxlcyBkaWZlcmVuY2lhbiBsb3MgZ3J1cG9zLiBBIHBlc2FyIGRlIHF1ZSBubyBlcyBuZWNlc2FyaW8gZmlsdHJhciBsYXMgdmFyaWFibGVzLCBzZSByZWNvbWllbmRhIGhhY2VybG8sIHRlbmllbmRvIHByZXNlbnRlIGJhc2VzIHRlw7NyaWNhcyBvIGVtcMSxzIFyaWNhcywgcGFyYSB1biBtZWpvciBkZXNhcnJvbGxvIGRlbCBwcm9jZXNvLgoKIyMgQXNwZWN0b3MgcHJldmlvczogVGFtYcOxbyBkZSBsYSBtdWVzdHJhClNlIHJlY29taWVuZGEgbm8gdXRpbGl6YXIgdGFtYcOxb3MgZGUgbXVlc3RyYSBwZXF1ZcOxb3MsIGRhZG8gcXVlIHNlIGFmZWN0YSBsYSBlc3RhYmlsaWRhZCB5IGdlbmVyYWxpemFjacOzbiBkZSBsb3MKcmVzdWx0YWRvcywgYWRlbcOhcyBwb3JxdWUgc2UgcmVxdWllcmUgY29uZmlhYmlsaWRhZCBlbiBsb3MgcmVzdWx0YWRvcyBxdWUgcG9zdGVyaW9ybWVudGUgc2UgYXBsaWNhcsOhbiBhIG90cmFzIG11ZXN0cmFzIHBvciBtZWRpbyBkZSBsYSB2YWxpZGFjacOzbiBjcnV6YWRhLiBTZSByZWNvbWllbmRhIHF1ZSBsYSB0b3RhbGlkYWQgZGUgbGFzIG11ZXN0cmEgKCRcZGlzcGxheXN0eWxlXFNpZ21hX2kgbl9pJCApIHNlYSBhbCBtZW5vcyAxMCB2ZWNlcyBlbCBuw7ptZXJvIGRlIHZhcmlhYmxlcyBkaXNjcmltaW5hbnRlcy4KCiMjIEFzcGVjdG9zIHByZXZpb3M6IFN1cHVlc3RvcwpMb3MgZGF0b3MgY29uIGxvcyBjdWFsZXMgc2UgdHJhYmFqYXLDoSBkZWJlbiBjdW1wbGlyIGxvcyBzaWd1aWVudGVzIHN1cHVlc3RvczoKKiBJbmRlcGVuZGVuY2lhIGRlIGxhcyBvYnNlcnZhY2lvbmVzICh2aXRhbCBpbXBvcnRhbmNpYSkuCiogTm9ybWFsaWRhZCBtdWx0aXZhcmlhZGEuIFBhcmEgZWwgYW7DoWxpc2lzIGRlc2NyaXB0aXZvIG5vIGVzIG5lY2VzYXJpYSwgbWFzIHBhcmEgZWwgcHJlZGljdGl2byBlcyBpbmRpc3BlbnNhYmxlLgoqIEhvbW9nZW5laWRhZCBkZSBsYXMgbWF0cmljZXMgZGUgY292YXJpYW56YXMuIFNlIGFjZXB0YSBsYSB2aW9sYWNpw7NuIGRlIGVzdGUgc3VwdWVzdG8gZW4gc2kgZWwgcmF0aW8gZGVsIHRhbWHDsW8gZGVsIGdydXBvIG3DoXMgZ3JhbmRlIGRpdmlkbyBwb3IgZWwgZGVsIG3DoXMgcGVxdWXDsW8gZXMgbWVub3IgYSAxLjUuCgojIyBDb21wb25lbnRlcyBkZWwgcmVzdWx0YWRvOiBDb2VmaWNpZW50ZXMKRWwgQW7DoWxpc2lzIERpc2NyaW1pbmFudGUgY2FsY3VsYSBtYXRlbcOhdGljYW1lbnRlIGxvcyBwZXNvcyBwYXJhIGxhcyBwdW50dWFjaW9uZXMgZW4gY2FkYSB2YXJpYWJsZSBkaXNjcmltaW5hZG9yYSBxdWUgcmVmbGVqYSBlbCBncmFkbyBlbiBxdWUgbGFzIHB1bnR1YWNpb25lcyBkZSBkaWNoYSB2YXJpYWJsZSBkaWZpZXJlbiBlbnRyZSBsb3MgZ3J1cG9zIHF1ZSBzb24gZGlzY3JpbWluYWRvcywgbGFzIHZhcmlhYmxlcyBxdWUgbcOhcyBkaWZpZXJhbiB0ZW5kcsOhbiBtYXlvciBwZXNvLiBBIGVzdG9zIHBlc29zIHNlIGxlcyBjb25vY2UgY29tbyBjb2VmaWNpZW50ZXMgZGlzY3JpbWluYW50ZXMuCgojIyBDb21wb25lbnRlcyBkZWwgcmVzdWx0YWRvOiBGdW5jacOzbiBkaXNjcmltaW5hbnRlcywgcHVudHVhY2lvbmVzIHkgY2VudHJvaWRlcwpFbCBBbsOhbGlzaXMgRGlzY3JpbWluYW50ZSBmb3JtYSB1bmEgbyBtw6FzIGNvbWJpbmFjaW9uZXMgbGluZWFsZXMgcG9uZGVyYWRhcyBkZSBsYXMgdmFyaWFibGVzIGRpc2NyaW1pbmFudGVzIGxsYW1hZGFzCmZ1bmNpb25lcyBkaXNjcmltaW5hbnRlcy4gTGEgcHVudHVhY2nDs24gZGlzY3JpbWluYW50ZSBwYXJhIGNhZGEgZnVuY2nDs24gZXMgY2FsY3VsYWRhIHBhcmEgY2FkYSBjYXNvIGVuIGxhIG11ZXN0cmEuIEx1ZWdvCnNlIGNhbGN1bGEgZWwgcHVudGFqZSBkaXNjcmltaW5hbnRlIHByb21lZGlvIGRlIGxvcyBjYXNvcyBxdWUgcGVydGVuZWNlbiBhIHVuIGdydXBvIChsb3MgY2VudHJvaWRlcyBkZSBsb3MgZ3J1cG9zKSBwYXJhCmNhZGEgZnVuY2nDs24gZGlzY3JpbWluYW50ZS4gUGFyYSBmaW5lcyBjbGFzaWZpY2F0b3Jpb3MgeSBwcmVkaWN0aXZvcywgZWwgcHVudGFqZSBkaXNjcmltaW5hbnRlIHBhcmEgY2FkYSBjYXNvIHBvc2libGUgZGUKdW4gZ3J1cG8gZXMgY29tcGFyYWRvIGNvbiBlbCBjZW50cm9pZGUgZGVsIG1pc21vLCB5IHNlIGNhbGN1bGEgdW5hIHByb2JhYmlsaWRhZCBkZSBwZXJ0ZW5lbmNpYSBhbCBncnVwby4gRW50cmUgbcOhcwpjZXJjYW5vIGVsIHB1bnRhamUgc2UgZW5jdWVudHJlIGFsIGNlbnRyb2lkZSBkZSB1biBncnVwbywgbWF5b3IgcHJvYmFiaWxpZGFkIHRpZW5lIGRlIHBlcnRlbmVjZXIgYSDDqXN0ZS4gTG9zIGNlbnRyb2lkZXMKZGUgZ3J1cG8gcmV2ZWxhbiBxdWUgdGFudG8geSBkZSBxdWUgbWFuZXJhIGxvcyBncnVwb3Mgc29uIGRpZmVyZW5jaWFkb3MgZW4gY2FkYSBmdW5jacOzbi4gTGEgbWFnbml0dWQgZW4gdmFsb3IgYWJzb2x1dG8KZGUgbG9zIGNlbnRyb2lkZXMgZGUgZ3J1cG8gaW5kaWNhbiBlbCBncmFkbyBlbiBlbCBjdWFsIHVuIGdydXBvIGVzIGRpZmVyZW5jaWFkbyBlbiB1bmEgZnVuY2nDs24sIHkgZWwgc2lnbm8gbGEgZGlyZWNjacOzbiBkZQpkaWZlcmVuY2lhY2nDs24uCgojIyBGdW5jacOzbiBkaXNjcmltaW5hbnRlIHBhcmEgZG9zIGdydXBvcwpTZSBhc3VtZSBxdWUgbG9zIGRvcyBncnVwb3MgcG9zZWVuIGxhIG1pc21hIG1hdHJpeiBkZSBjb3ZhcmlhbnphICRcU2lnbWEkIHBlcm8gZGlmZXJlbnRlcyB2ZWN0b3JlcyBkZSBtZWRpYSAkXG11XzEsIFxtdV8yJC4KClNlYW4gJHlfezExfSx5X3sxMn0sLi4uLHlfezFuXzF9JCB5ICR5X3syMX0seV97MjJ9LC4uLix5X3sybl8yfSQgY2Fzb3MgcGFyYSBsb3MgZG9zIGdydXBvcyBvIHBvYmxhY2lvbmVzLCBkb25kZSBjYWRhIHZlY3RvciAkeV97aWp9JCBjb25zaXN0ZSBlbiBtZWRpY2lvbmVzIHBhcmEgbGFzICRwJCB2YXJpYWJsZXMuIExhIGZ1bmNpw7NuIGRpc2NyaW1pbmFudGUgZXMgbGEgY29tYmluYWNpw7NuIGxpbmVhbCBkZSBsYXMgJHAkIHZhcmlhYmxlcyBxdWUgbWF4aW1pemEgbGEgZGlzdGFuY2lhIGVudHJlIGxhcyBtZWRpYXMgZGUgbG9zIGRvcyBncnVwb3MgKHRyYW5zZm9ybWFkb3MpLgokJCB6X3sxaX0gPSBhJ3lfezFpfSA9IGFfMXlfezFpMX0rYV8yeV97MWkyfSsuLi4rYV9weV97MWlwfSBccXF1YWQgaT0xLC4uLixuXzEgJCQKJCQgel97Mml9ID0gYSd5X3syaX0gPSBhXzF5X3syaTF9K2FfMnlfezJpMn0rLi4uK2FfcHlfezJpcH0gXHFxdWFkIGk9MSwuLi4sbl8yICQkCgpjdXlhcyByZXNwZWN0aXZhcyBtZWRpYXMgc29uICRcZGlzcGxheXN0eWxlXGJhcnt6XzF9ID0gXHN1bV97aT0xfV57bl8xfSBcZnJhY3t6X3sxaX19e25fMX0gPSBhJ1xiYXJ7eV8xfSQgeSAkXGJhcnt6XzJ9PWEnXGJhcnt5XzJ9JC4KClNlYSAkYSQgZWwgdmVjdG9yIHF1ZSBtYXhpbWl6YSBsYSBkaWZlcmVuY2lhIGVzdGFuZGFyaXphZGEgYWwgY3VhZHJhZG8gJChcYmFye3pfMX0tXGJhcnt6XzJ9KV4yL3Nfel4yJC4KJCRcZnJhY3soXGJhcnt6XzF9LVxiYXJ7el8yfSleMn17c196XjJ9ID0gXGZyYWN7W2EnKFxiYXJ7eV8xfS1cYmFye3lfMn0pXV4yfXthJ1Nfe3BsfWF9JCQKZWwgbcOheGltbyBvY3VycmUgZW4gCiQkYSA9IFNfe3BsfV57LTF9KFxiYXJ7eV8xfS1cYmFye3lfMn0pJCQKbyBjdWFuZG8gJGEkIGVzIHVuIG3Dumx0aXBsbyBkZSAkU197cGx9XnstMX0oXGJhcnt5XzF9LVxiYXJ7eV8yfSkkLiBFc3RvIHF1aWVyZSBkZWNpciBxdWUgZWwgJGEkIG3DoXhpbW8gbm8gZXMgw7puaWNvLCBwZXJvIGxhIGRpcmVjY2nDs24gc8OtLlxcIAoKTGEgZGlyZWNjacOzbiDDs3B0aW1hIGRhZGEgcG9yICRTX3twbH1eey0xfShcYmFye3lfMX0tXGJhcnt5XzJ9KSQgZXMgcGFyYWxlbGFtZW50ZSBlZmljaWVudGUgYSBsYSBsw61uZWEgcXVlIHVuZSAkXGJhcnt5XzF9JCB5ICRcYmFye3lfMn0kIHBvcnF1ZQokJFxmcmFjeyhcYmFye3pfMX0tXGJhcnt6XzJ9KV4yfXtzX3peMn0gPSAoXGJhcnt5XzF9LVxiYXJ7eV8yfSknU197cGx9KFxiYXJ7eV8xfS1cYmFye3lfMn0pJCQKCnBhcmEgJHo9YSd5JCBjb24gJGE9U197cGx9XnstMX0oXGJhcnt5XzF9LVxiYXJ7eV8yfSkkCgohW10oaHR0cHM6Ly93cml0ZWxhdGV4LnMzLmFtYXpvbmF3cy5jb20veHRrZHh6anNzd25yL3VwbG9hZHMvNDI4NS8xMjY3MzA0My8xLlBORykKCkxhcyBjb21iaW5hY2lvbmVzIGxpbmVhbGVzICR6X3sxaX09YSd5X3sxaX09YV8xeV97MWkxfSthXzJ5ezFpMn0kIHkgJHpfezJpfT1hJ3lfezJpfT1hXzF5X3syaTF9K2FfMnlfezJpMn0kIHByb3llY3RhIGxvcyBwdW50b3MgJHlfezFpfSx5X3syaX0kIGVuIGxhIGzDrW5lYSBxdWUgbWF4aW1pemEgbGEgc2VwYXJhY2nDs24gZGUgbG9zIGRvcyBncnVwb3MuIERhZG8gcXVlIGxhcyB2YXJpYWJsZXMgJHlfMSx5XzIkIHNvbiBub3JtYWwgYml2YXJpYWRhcywgdW5hIGNvbWJpbmFjacOzbiBsaW5lYWwgJHo9YV8xeV8xK2FfMnlfMj1hJ3kkIGVzIG5vcm1hbCB1bml2YXJpYWRhLgoKIyMgRnVuY2nDs24gZGlzY3JpbWluYW50ZSBwYXJhIG3Dumx0aXBsZXMgKGspIGdydXBvcwpTZSBkZXNlYSBlbmNvbnRyYXIgbGEgY29tYmluYWNpw7NuIGxpbmVhbCBkZSB2YXJpYWJsZXMgcXVlIG1lam9yIHNlcGFyZSBsb3MgJGskIGdydXBvcyBkZSBtw7psdGlwbGVzIG9ic2VydmFjaW9uZXMuCgpTZWEgJG5faSQgZWwgbsO6bWVybyBkZSBvYnNlcnZhY2lvbmVzIHBhcmEgZWwgJGkkLcOpc2ltbyBncnVwby4gQ2FkYSB2ZWN0b3IgZGUgb2JzZXJ2YWNpw7NuICR5X3tpan0kIHNlIHRyYW5zZm9ybWEgcGFyYSBvYnRlbmVyIHVuICR6X3tpan09YSd5X3tpan07aT0xLC4uLixrO2o9MSwuLi4sbl9pJCB5IHNlIGhhbGxhbiBsYXMgbWVkaWFzICRcYmFye3pfaX09YSdcYmFye3lfaX0kLiBDb21vIHBhcmEgZWwgY2FzbyBkZSBkb3MgZ3J1cG9zLCBzZSBidXNjYSBlIHZlY3RvciAkYSQgcXVlIG1heGltaWNlIGxhIHNlcGFyYWNpw7NuIGVudHJlICRcYmFye3pfMX0sXGJhcnt6XzJ9LC4uLixcYmFye3pfa30kLgoKUmV0b21hbmRvIGxhIGVjdWFjacOzbiwgc2UgdGllbmUKJCRcZnJhY3soXGJhcnt6XzF9LVxiYXJ7el8yfSleMn17c196XjJ9ID0gXGZyYWN7W2EnKFxiYXJ7eV8xfS1cYmFye3lfMn0pXV4yfXthJ1Nfe3BsfWF9PVxmcmFje2EnKFxiYXJ7eV8xfS1cYmFye3lfMn0pKFxiYXJ7eV8xfS1cYmFye3lfMn0pJ2F9e2EnU197cGx9YX0kJAoKQWhvcmEsIHBhcmEgZXh0ZW5kZXIgbG8gYW50ZXJpb3IgYSAkayQgZ3J1cG9zLCBzZSB1c2FuIGxhcyBtYXRyaWNlcyBkZSBNQU5PVkEgJEgkIGVuIGx1Z2FyIGRlICQoXGJhcnt5XzF9LVxiYXJ7eV8yfSkoXGJhcnt5XzF9LVxiYXJ7eV8yfSknJCB5ICRFJCBlbiBsdWdhciBkZSAkU197cGx9JCwgdGVuaWVuZG8KJCQKXGxhbWJkYSA9IFxmcmFje2EnSGF9e2EnRWF9JCQgJCQKXGxhbWJkYSA9XGZyYWN7U1NIKHopfXtTU0Uoeil9ICQkCmRvbmRlICRTU0goeikkIHkgJFNTRSh6KSQgc29uIGxhcyBzdW1hcyBlbnRyZSB5IGFsIGludGVyaW9yIGRlIGxvcyBjdWFkcmFkb3MgZGUgJHokCgpMYSBlY3VhY2nDs24gYW50ZXJpb3Igc2UgcHVlZGUgcmVlc2NyaWJpciBjb21vIAokJCBhJ0hhPVxsYW1iZGEgYSdFYSAkJAokJCBhJyhIYS1cbGFtYmRhIEVhKT0wICQkCgpTZSB2ZSBjbGFyYW1lbnRlIHF1ZSBleGlzdGVuIGRvcyBwb3NpYmxlcyBzb2x1Y2lvbmVzIGRlbCBzaXN0ZW1hLCBkZSBsYXMgY3VhbGVzICRhJz0wJyQgc2UgZGVzY2FydGEgcG9ycXVlIHByb2R1Y2UgJFxsYW1iZGE9MC8wJC4gTGEgb3RyYSBwb3NpYmlsaWRhZCBlcyAKCiQkSGEtXGxhbWJkYSBFYSA9IDAkJAoKJCQoRV57LTF9SC1cbGFtYmRhIEkpYT0wJCQKCgpjdXlhIHNvbHVjacOzbiBzb24gbG9zIHZhbG9yZXMgcHJvcGlvcyAkXGxhbWJkYV8xLFxsYW1iZGFfMiwuLi4sXGxhbWJkYV9zJCB5IHN1cyB2ZWN0b3JlcyBwcm9waW9zIGFzb2NpYWRvcyAkYV8xLGFfMiwuLi4sYV9zJCBkZSAkRV57LTF9SCQuIEVsIG7Dum1lcm8gJHMkIGRlIHZhbG9yZXMgcHJvcGlvcyAoJ25vbi16ZXJvcycpIGVzIGVsIHJhbmdvIGRlIGxhIG1hdHJpeiBILCBxdWUgc2UgcHVlZGUgZW5jb250cmFyIGNvbW8gZWwgJG1pblxsYnJhY2Ugay0xLHBccmJyYWNlJC4gTG9zICRcbGFtYmRhJHMgc2UgZW5jdWVudHJhbiBvcmRlbmFkb3MgZW4gZm9ybWEgZGVzY2VuZGVudGUsIHNpZW5kbyAkel8xPWEnXzF5JCBsYSBmdW5jacOzbiBkaXNjcmltaW5hbnRlIHF1ZSBtYXhpbWFsbWVudGUgc2VwYXJhIGxhcyBtZWRpYXMuCgpEZSBsb3MgdmVjdG9yZXMgcHJvcGlvcyAkYV8xLGFfMiwuLi4sYV9zJCBzZSBvYnRpZW5lbiAkcyQgZnVuY2lvbmVzIGRpc2NyaW1pbmFudGVzICR6XzE9YSdfMSx6XzI9YSdfMiwuLi4sel9zPWEnX3MkLCBxdWUgbXVlc3RyYW4gbGFzIGRpbWVuc2lvbmVzIG8gZGlyZWNjaW9uZXMgZW4gbGFzIGRpZmVyZW5jaWFzIGVudHJlICRcYmFye3lfMX0sXGJhcnt5XzJ9LC4uLixcYmFye3lfa30kLiBFc3RhcyBmdW5jaW9uZXMgbm8gZXN0w6FuIGNvcnJlbGFjaW9uYWRhcyBuaSBzb24gb3J0b2dvbmFsZXMgcG9ycXVlICRFXnstMX1IJCBubyBlcyBzaW3DqXRyaWNhLiAKCiMjIEZ1bmNpb25lcyBkaXNjcmltaW5hbnRlcyBlc3RhbmRhcml6YWRhcwpTaSBsYXMgJHkkcyBubyBzb24gY29ubWVuc3VyYWJsZXMsIGVzIGRlY2lyLCBubyBwb3NlZW4gbGEgbWlzbWEgZXNjYWxhIHkgc3VzIHZhcmlhbnphcyBzb24gaW5jb21wYXJhYmxlcywgY29uIGdyYW5kZXMgZGlmZXJlbmNpYXMgZW50cmUgc8OtOyBzZSByZXF1aWVyZW4gY29lZmljaWVudGVzICRhXipfciQgcXVlIHNlIGFwbGlxdWVuIGEgdmFyaWFibGVzIGVzdGFuZGFyaXphZGFzLgoKQ29uc2lkZXJlIGVsIGNhc28gcGFyYSBkb3MgZ3J1cG9zLiBQYXJhIGVsICRpJC3DqXNpbW8gdmVjdG9yIGRlIG9ic2VydmFjacOzbiAkeV97MWl9JCBvICR5X3syaX0kIGVuIGVsIGdydXBvIDEgbyAyLCBsYSBmdW5jacOzbiBkaXNjcmltaW5hbnRlIGVuIHTDqXJtaW5vcyBkZSBsYXMgdmFyaWFibGVzIGVzdGFuZGFyaXphZGFzIHNlcsOtYQoKJCQKel97MWl9PWFeKl8xXGZyYWN7eV97MWkxfS1cYmFye3lfezExfX19e3NfMX0rYV4qXzJcZnJhY3t5X3sxaTJ9LVxiYXJ7eV97MTJ9fX17c18yfSsuLi4rYV4qX3BcZnJhY3t5X3sxaXB9LVxiYXJ7eV97MXB9fX17c19wfSBccXF1YWQgaT0xLDIsLi4uLG5fMSQkICQkCnpfezFpfT1hXipfMVxmcmFje3lfezJpMX0tXGJhcnt5X3syMX19fXtzXzF9K2FeKl8yXGZyYWN7eV97MmkyfS1cYmFye3lfezIyfX19e3NfMn0rLi4uK2FeKl9wXGZyYWN7eV97MmlwfS1cYmFye3lfezJwfX19e3NfcH0gXHFxdWFkIGk9MSwyLC4uLixuXzIgJCQKCmRvbmRlICRcYmFye3knXzF9PShcYmFye3lfezExfX0sXGJhcnt5X3sxMn19LC4uLixcYmFye3lfezFwfX0pJCB5ICRcYmFye3knXzJ9PShcYmFye3lfezIxfX0sXGJhcnt5X3syMn19LC4uLixcYmFye3lfezJwfX0pJCBzb24gbG9zIHZlY3RvcmVzIGRlIG1lZGlhcyBwYXJhIGxvcyBkb3MgZ3J1cG9zLCB5ICRzX3IkIGVzIGxhIGRlc3ZpYWNpw7NuIGVzdMOhbmRhciBlbnRyZSBtdWVzdHJhIGRlIGxhICRyJC3DqXNpbWEgdmFyaWFibGUgcXVlIHNlIG9idGllbmUgZGUgbGEgcmHDrXogZGVsICRyJC3DqXNpbW8gZWxlbWVudG8gZGUgbGEgZGlhZ29uYWwgZGUgJFNfe3BsfSQuCgpDbGFyYW1lbnRlIGxvcyBjb2VmaWNpZW50ZXMgZXN0YW5kYXJpemFkb3Mgc29uIGRlIGxhIGZvcm1hCgokJGFeKl9yPXNfcmFfciBccXF1YWQgcj0xLDIsLi4uLHAgJCQKeSB2ZWN0b3JpYWxtZW50ZSBjb21vCiQkCmFeKj0oZGlhZyhTX3twbH0pKV57MS8yfWEKJCQKCkFob3JhIGNvbnNpZGVyZSBlbCBjYXNvIHBhcmEgbcO6bHRpcGxlcyBncnVwb3MuIFNpIHNlIGRlbm90YSBhbCAkciQtw6lzaW1vIGNvZWZpY2llbnRlIGRlIGxhICRtJC3DqXNpbWEgZnVuY2nDs24gZGlzY3JpbWluYW50ZSBjb21vICRhXipfe21yfSQsIHNlIHRpZW5lCiQkYV4qX3ttcn09c19yYV97bXJ9IFxxcXVhZCBtPTEsMiwuLi4scztyPTEsMiwuLi4scCQkCmRvbmRlICRzX3IkIHNlIG9idGllbmUgZGUgJFNfe3BsfT1cZnJhY3tFfXt2X3tFfX0kLlxcCgpEYWRvIHF1ZSBlbCAkbSQtw6lzaW1vIHZlY3RvciBwcm9waW8gZXMgw7puaWNvIGhhc3RhIGxhIG11bHRpcGxpY2FjacOzbiBwb3IgdW4gZXNjYWxhciwgbGEgZXhwcmVzacOzbiBhbnRlcmlvciBzZSBwdWVkZSBzaW1wbGlmaWNhciBjb21vCiQkYV4qX3ttcn09XHNxcnRbXXtlX3tycn19YV97bXJ9JCQKZG9uZGUgJGVfe3JyfSQgZXMgZWwgJHIkLcOpc2ltbyBlbGVtZW50byBkZSBsYSBkaWFnb25hbCBkZSAkRSQuCgojIyMgSW1wb3J0YW5jaWEgcmVsYXRpdmEKTGEgaW1wb3J0YW5jaWEgcmVsYXRpdmEgZGUgY2FkYSBmdW5jacOzbiBkaXNjcmltaW5hbnRlIGVzdMOhIGRhZGEgcG9yCiQkXGZyYWN7XGxhbWJkYV9pfXtcc3VtX3tqPTF9XntzfVxsYW1iZGFfan0kJApBbCBpZ3VhbCBxdWUgZW4gQ29tcG9uZW50ZXMgUHJpbmNpcGFsZXMsIGRvcyBvIHRyZXMgZnVuY2lvbmVzIGRpc2NyaW1pbmFudGVzIGdlbmVyYWxtZW50ZSBvZnJlY2VuIHN1ZmljaWVudGUgaW5mb3JtYWNpw7NuIHBhcmEgZXhwbGljYXIgbGFzIGRpZmVyZW5jaWFzIGVudHJlIGxvcyBncnVwb3MgKHNlIGJ1c2NhIHVuYSBwcm9wb3JjacOzbiBhY3VtdWxhZGEgZGUgMC44KS4gCgojIyBUZXN0cyBkZSBzaWduaWZpY2FuY2lhCkEgZGlmZXJlbmNpYSBhbCBwcm9jZXNvIGRlbCBjw6FsY3VsbyBkZSBsYSBmdW5jacOzbiBkaXNjcmltaW5hbnRlLCBwYXJhIGxvcyB0ZXN0IGRlIHNpZ25pZmljYW5jaWEgc2UgcmVxdWllcmUgZGVsIHN1cHVlc3RvIGRlIG5vcm1hbGlkYWQgbXVsdGl2YXJpYWRhLgoKIyMjIENhc28gZGUgZG9zIGdydXBvcwpBbnRlcmlvcm1lbnRlLCBzZSB2aW8gcXVlIGxhIHNlcGFyYWNpw7NuIGRlIGxhcyBtZWRpYXMgZGUgbGFzIHZhcmlhYmxlcyB0cmFuc2Zvcm1hZGFzIG9idGVuaWRhIHBvciBsYSBmdW5jacOzbiBkaXNjcmltaW5hbnRlICR6PWEneSQgZXMgZXF1aXZhbGVudGUgYSBsYSBkaXN0YW5jaWEgZXN0YW5kYXJpemFkYSBkZSBsb3MgdmVjdG9yZXMgZGUgbWVkaWFzICRcYmFye3lfMX0sXGJhcnt5XzJ9JC4gRXN0YSBkaXN0YW5jaWEgZXN0YW5kYXJpemFkYSBlcyBwcm9wb3JjaW9uYWwgYSBsYSAkVF4yJCBwYXJhIGRvcyBncnVwb3MgJCRUXjI9XGZyYWN7bl8xbl8yfXtuXzErbl8yfSAoXGJhcnt5XzF9LVxiYXJ7eV8yfSknU14oLTEpX3BsKFxiYXJ7eV8xfS1cYmFye3lfMn0pJCQgQXPDrSwgZWwgdmVjdG9yIGRlIGNvZWZpY2llbnRlcyAkYSQgZGUgbGEgZnVuY2nDs24gZGlzY3JpbWluYW50ZSBlcyBzaWduaWZpY2F0aXZhbWVudGUgZGlmZXJlbnRlIGRlICQwJCBzaSAkVF4yJCBlcyBzaWduaWZpY2F0aXZhLiAgCiQkSF8wIDogXGFscGhhPTAgXGVxdWl2IEhfMCA6XG11XzE9XG11XzIgXHFxdWFkIFx0ZXh0e2RvbmRlIH0gXGFscGhhID0gXFNpZ21hXnstMX0oXG11XzEtXG11XzIpJCQKClBhcmEgcHJvYmFyIGxhIHNpZ25pZmljYW5jaWEgZGUgdW4gc3ViY29uanVudG8gZGUgY29lZmljaWVudGVzIGRlIGxhIGZ1bmNpw7NuIGRpc2NyaW1pbmFudGUsIHNlIHB1ZWRlIHVzYXIgZWwgdGVzdCBkZSBhbsOhbGlzaXMgZGVsIHBlcmZpbCBwYXJhIGVsIGNvcnJlc3BvbmRpZW50ZSBzdWJjb25qdW50byBkZSBsYXMgJHkkcy4KCiMjIyBDYXNvIGRlIG3Dumx0aXBsZXMgZ3J1cG9zClNlIGRlbm90w7MgcXVlIGVsIGNyaXRlcmlvIGRpc2NyaW1pbmFudGUgJFxsYW1iZGE9YSdIYS9hJ0VhJCBlcyBtYXhpbWl6YWRvIHBvciAkXGxhbWJkYV8xJCwgZWwgbWF5b3IgdmFsb3IgcHJvcGlvIGRlICRFXnstMX1IJCwgeSBsb3MgcmVzdGFudGVzIHZhbG9yZXMgcHJvcGlvcyAkXGxhbWJkYV8yLC4uLixcbGFtYmRhX3MkIGNvcnJlc3BvbmRpZW50ZXMgYSBsYXMgb3RyYXMgZGltZW5zaW9uZXMgZGlzY3JpbWluYW50ZXMuIEVzdG9zIHZhbG9yZXMgcHJvcGlvcyBzb24gbG9zIG1pc21vcyBxdWUgbG9zIGRlbCB0ZXN0ICRcTGFtYmRhJCBkZSBXaWxrcyBwYXJhIGRpZmVyZW5jaWFzIHNpZ25pZmljYXRpdmFzIGVudHJlIGRlIGxvcyB2ZWN0b3JlcyBkZSBtZWRpYXMKJCQKXExhbWJkYV8xPVxwcm9kX3tpPTF9XntzfVxmcmFjezF9ezErXGxhbWJkYV9pfSBcc2ltIFxMYW1iZGFfe3Asay0xLE4ta30KJCQKZG9uZGUgJE49XHN1bV9pIG5faSQuIERhZG8gcXVlICRcTGFtYmRhXzEkIGVzIHBlcXVlw7FvIHNpIHVubyBvIG3DoXMgJFxsYW1iZGFfaSRzIHNvbiBncmFuZGVzLCAkXExhbWJkYSQgZGUgV2lsa3MgcHJ1ZWJhIGxhIHNpZ25pZmljYW5jaWEgZGUgbG9zIHZhbG9yZXMgcHJvcGlvcyB5IGRlIGVzdGUgbW9kbyBkZSBsYXMgZnVuY2lvbmVzIGRpc2NyaW1pbmFudGVzLiBMb3MgJHMkIHZhbG9yZXMgcHJvcGlvcyByZXByZXNlbnRhbiBsYXMgJHMkIGRpbWVuc2lvbmVzIGRlIHNlcGFyYWNpw7NuIGRlIGxvcyB2ZWN0b3JlcyBkZSBtZWRpYXMgJFxiYXJ7eV8xfSxcYmFye3lfMn0sLi4uLFxiYXJ7eV9rfSQuCgpUYW1iacOpbiBzZSBwdWVkZSB1dGlsaXphciB1bmEgYXByb3hpbWFjacOzbiAkXGNoaV4yJCBwYXJhICRcTGFtYmRhXzEkIGRhZGEgcG9yCiQkClZfMT0tW3ZfRS1cZnJhY3sxfXsyfShwLXZfSCsxKV1cbG9nXExhbWJkYV8xJCQgJCQKPS1bTi0xLVxmcmFjezF9ezJ9KHArayldXGxvZ1xwcm9kX3tpPTF9XnNcZnJhY3sxfXsxK1xsYW1iZGFfaX0kJCAkJAo9W04tMS1cZnJhY3sxfXsyfShwK2spXVxzdW1fe2k9MX1ecyBcbG9nKDErXGxhbWJkYV9pKSBcc2ltIFxjaGleMl97cChrLTEpfQokJAoKRWwgdGVzdCBlc3RhZMOtc3RpY28gbWlkZW4gbGEgc2lnbmlmaWNhbmNpYSBkZSAkXGxhbWJkYV8xLFxsYW1iZGFfMiwuLi4sXGxhbWJkYV9zJC4gU2kgc2UgcmVjaGF6YSAkSF8wJCwgc2UgY29uY2x1eWUgcXVlIGFsIG1lbm9zIHVuICRcbGFtYmRhJCBlcyBzaWduaWZpY2F0aXZhbWVudGUgZGlmZXJlbnRlIGRlIGNlcm8sIHkgaGF5IGFsIG1lbm9zIHVuYSBkaW1lbnNpw7NuIGRlIHNlcGFyYWNpw7NuIGRlIGxvcyB2ZWN0b3JlcyBkZSBtZWRpYXMuIENvbW8gJFxsYW1iZGFfMSQgZXMgZWwgbWF5b3IsIHNvbG8gc2UgZXN0w6Egc2VndXJvIGRlIHN1IHNpZ25pZmljYW5jaWEsIGp1bnRvIGNvbiBsYSBkZSAkel8xPWEnXzF5JC4KCkRhZG8gcXVlIHNlIGVzdMOhIGludGVyZXNhZG8gZW4gc2FiZXIgY3XDoWxlcyBkZSBlc3RhcyBkaW1lbnNpb25lcyBzb24gc2lnbmlmaWNhdGl2YXMsIHNlIGhhY2UgZWwgdGVzdCAkXExhbWJkYSQgZGUgV2lsa2lucyBpdGVyYXRpdmFtZW50ZSBoYXN0YSBxdWUgJEhfMCQgc2VhIGFjZXB0YWRhLiBMYXMgZGltZW5zaW9uZXMgc2lnbmlmaWNhdGl2YXMgcmVzdWx0YW50ZXMgc2Vyw61hbiBsYXMgYXNvY2lhZGFzIGEgJFxsYW1iZGFfMSwuLi4sXGxhbWJkYV97ci0xfSQsIHNpZW5kbyAkciQgbGEgZXRhcGEgZGUgYWNlcHRhY2nDs24gZGUgJEhfMCQuCgpFbCBlc3RhZMOtc3RpY28gZW4gbGEgZXRhcGEgJG0kLCBlcyBkZWNpciBwYXJhIHByb2JhciBsYSBzaWduaWZpY2FuY2lhICBkZSAkXGxhbWJkYV9tLC4uLixcbGFtYmRhX3MkLCBlcwokJApcTGFtYmRhX209XHByb2Rfe2k9bX1ee3N9XGZyYWN7MX17MStcbGFtYmRhX2l9IFxzaW0gXExhbWJkYV97cC1tKzEsay1tLE4tay1tKzF9CiQkCnkgc3UgYXByb3hpbWFjacOzbiAkXGNoaV4yJCBlcwokJApWX209LVtOLTEtXGZyYWN7MX17Mn0ocCtrKV1cbG9nXExhbWJkYV9tICQkICQkCj1bTi0xLVxmcmFjezF9ezJ9KHArayldXHN1bV97aT1tfV5zIFxsb2coMStcbGFtYmRhX2kpIFxzaW0gXGNoaV4yX3socC1tKzEpKGstbSl9CiQkCgpFbiBtdWNob3MgY2Fzb3MsIGVzdGUgdGVzdCBhcnJvamEgbcOhcyAkXGxhbWJkYSRzIHNpZ25pZmljYXRpdm9zIHF1ZSBsb3MgcXVlIHNlIHB1ZWRlbiBjb25zaWRlcmFyIGRlIGltcG9ydGFuY2lhIHByw6FjdGljYS4gU2kgJFxsYW1iZGFfaS9cc3VtX2pcbGFtYmRhX2okIGVzIHBlcXVlw7FvLCBsYSBmdW5jacOzbiBkaXNjcmltaW5hbnRlIGFzb2NpYWRhIHBvZHLDrWEgbm8gc2VyIGRlIGludGVyw6lzLCBhIHBlc2FyIGRlIHNlciBzaWduaWZpY2FudGUuXFwKClRhbWJpw6luIHNlIHB1ZWRlIHV0aWxpemFyIHVuYSBhcHJveGltYWNpw7NuICRGJC4gUGFyYSAkXExhbWJkYV9tJCwgc2UgdXRpbGl6YSBlbCBlc3RhZMOtc3RpY28KJCQKRj1cZnJhY3sxLVxMYW1iZGFfbV57MS90fX17XExhbWJkYV9tXnsxL3R9fQokJApkb25kZSAKJCQKdCA9IFxzcXJ0W117XGZyYWN7KHAtbSsxKV4yKGstbSleMi00fXsocC1tKzEpXjIoay1tKV4yLTV9fSQkICQkCncgPSBOLTEtXGZyYWN7MX17Mn0ocCtrKSQkICQkCmRmXzEgPSAocC1tKzEpKGstbSkkJCAkJApkZl8yID0gd3QtXGZyYWN7MX17Mn1bKHAtbSsxKShrLW0pLTJdCiQkCgojIyBJbnRlcnByZXRhY2nDs24gZGUgZnVuY2lvbmVzIGRpc2NyaW1pbmFudGVzCgpFeGlzdGUgdW5hIGFsdGEgY29ycmVzcG9uZGVuY2lhIGVudHJlIGxhIGludGVycHJldGFjacOzbiBkZSBmdW5jaW9uZXMgZGlzY3JpbWluYW50ZXMgeSBsYSBkZXRlcm1pbmFjacOzbiBkZSBsYSBjb250cmlidWNpw7NuIGRlIGNhZGEgdmFyaWFibGUuIEVuIGxhIGludGVycHJldGFjacOzbiwgbG9zIHNpZ25vcyBkZSBsb3MgY29lZmljaWVudGVzIHNlIHRpZW5lbiBlbiBjdWVudGE7IG1pZW50cmFzIHF1ZSBlbiBkZXRlcm1pbmFyIGxhIGNvbnRyaWJ1Y2nDs24sIGxvcyBzaWdub3Mgc29uIGlnbm9yYWRvcyB5IHNlIHJhbmtlYW4gbG9zIGNvZWZpY2llbnRlcyBlbiB2YWxvciBhYnNvbHV0by4gR2VuZXJhbG1lbnRlLCBlbCBpbnRlcsOpcyByZWNhZSBlbiBlbCBwcm9jZXNvIGRlIGNvbnRyaWJ1Y2nDs24uIEEgY29udGludWFjacOzbiBzZSBwcmVzZW50YW4gdHJlcyBtw6l0b2RvcyBkaWZlcmVudGVzIHBhcmEgZGV0ZXJtaW5hciBsYSBjb250cmlidWNpw7NuIGRlIGNhZGEgdmFyaWFibGUgZW4gbGEgc2VwYXJhY2nDs24gZGUgbG9zIGdydXBvcy4KCiMjIyBNw6l0b2RvIDE6IENvZWZpY2llbnRlcyBlc3RhbmRhcml6YWRvcwpQYXJhIGNvbXBlbnNhciBsYSBkaWZlcmVuY2lhIGRlIGVzY2FsYSBlbnRyZSBsYXMgdmFyaWFibGVzLCBsb3MgY29lZmljaWVudGVzIHB1ZWRlbiBzZXIgZXN0YW5kYXJpemFkb3MgdXNhbmRvIGxhcyBlY3VhY2lvbmVzLCBkb25kZSBsb3MgY29lZmljaWVudGVzIHNvbiBhanVzdGFkb3MgeSBhcGxpY2FuIGEgdmFyaWFibGVzIGVzdGFuZGFyaXphZGFzLiBDb21vIGxhcyB2YXJpYWJsZXMgZXN0YW5kYXJpemFkYXMgJCh5X2tpcC1cYmFye3lfa3B9KS9zX3AkIHNvbiBsaWJyZXMgZGUgZXNjYWxhLCBsb3MgY29lZmljaWVudGVzIGVzdGFuZGFyaXphZG9zIHJlZmxlamFuIGNvcnJlY3RhbWVudGUgbGEgY29udHJpYnVjacOzbiBjb25qdW50YSBkZSBsYXMgdmFyaWFibGVzIGEgbGEgZnVuY2nDs24gZGlzY3JpbWluYW50ZSAkeiQgcXVlIG1heGltaXphIGxhIHNlcGFyYWNpw7NuIGRlIGxvcyBncnVwb3MuIEVsIHZhbG9yIGFic29sdXRvIGRlIGxvcyBjb2VmaWNpZW50ZXMgcHVlZGUgc2VyIHVzYWRvIHBhcmEgcmFua2VhciBsYXMgdmFyaWFibGVzIG5lIG9yZGVuIGEgc3UgY29udHJpYnVjacOzbiBhIGxhIGRpZmVyZW5jaWFjacOzbiBkZSBsb3MgZ3J1cG9zLiBQYXJhIHByb2Z1bmRpemFyLCBzZSBwdWVkZSBpbnRlcnByZXRhciBsYXMgZnVuY2lvbmVzIGRpc2NyaW1pbmFudGVzIHRlbmllbmRvIGVuIGN1ZW50YSBsb3Mgc2lnbm9zIGRlIGxvcyBjb2VmaWNpZW50ZXMuCgpMYSBmdW5jacOzbiBkaXNjcmltaW5hbnRlLCBhbCBpZ3VhbCBxdWUgb3RyYXMgY29tYmluYWNpb25lcyBsaW5lYWxlcywgZXN0w6Egc3VqZXRhIGEgY2llcnRhcyBsaW1pdGFjaW9uZXMgY29tbyBxdWUgbG9zIGNvZWZpY2llbnRlcyBkZSB1bmEgdmFyaWFibGUgcHVlZGVuIGNhbWJpYXIgcmFkaWNhbG1lbnRlIHNpIHVuYSB2YXJpYWJsZSBlcyBhZGljaW9uYWRhIG8gcmVtb3ZpZGEgeSBxdWUgZXN0b3MgcHVlZGVuIG5vIHNlciBlc3RhYmxlcyBzaSBlbCB0YW1hw7FvIGRlIGxhIG11ZXN0cmEgbm8gZXMgcmVsYXRpdmFtZW50ZSBncmFuZGUgZnJlbnRlIGFsIG7Dum1lcm8gZGUgdmFyaWFibGVzLiBTaSBlbCAkTi9wJCBlcyBtdXkgcGVxdWXDsW8sIGxhcyB2YXJpYWJsZXMgcXVlIHJhbmtlYW4gYWx0byBlbiB1bmEgbXVlc3RyYSwgbG8gcHVlZGVuIG5vIHNlciBlbiBvdHJhLiAKCiMjIyBNw6l0b2RvIDI6IFZhbG9yZXMgJEYkLXBhcmNpYWxlcwpQYXJhIGN1YWxxdWllciB2YXJpYWJsZSAkeV9yJCwgc2UgcHVlZGUgY2FsY3VsYXIgdW4gdGVzdCAkRiQtcGFyY2lhbCBtb3N0cmFuZG8gbGEgaW1wb3J0YW5jaWEgZGUgJHlfciQgZGVzcHXDqXMgZGUgYWp1c3RhciBhIGxhcyBvdHJhcyB2YXJpYWJsZXMsIGVuIG90cmFzIHBhbGFicmFzLCBsYSBzZXBhcmFjacOzbiBkYWRhIHBvciAkeV9yJCBlbiBjb250cmFwb3NpY2nDs24gYSBhcXVlbGxhIGRhZGEgcG9yIGxhcyBvdHJhcyB2YXJpYWJsZXMuXFwKClBhcmEgZWwgY2FzbyBkZSBkb3MgZ3J1cG9zLCBsYSAkRiQtcGFyY2lhbCBlc3TDoSBkYWRhIHBvcgokJEY9KHYtcCsxKVxmcmFje1ReMl9wLVReMl97cC0xfX17ditUXjJfe3AtMX19IFxzaW0gRl97MSx2LXArMX0kJApkb25kZSAkVF4yX3AkIGVzICRUXjIkIGRlIEhvdGVsbGluZyBwYXJhIGRvcyBtdWVzdHJhcyBjb24gdG9kYXMgbGFzICRwJCB2YXJpYWJsZXMsICRUXjJfe3AtMX0kIGVzIHVuIGVzdGFkw61zdGljbyAkVF4yJCBjb24gdG9kYXMgbGFzIHZhcmlhYmxlcyBleGNlcHR1YW5kbyAkeV9yJCwgeSAkdj1uXzErbl8yLTIkLlxcIAoKUGFyYSBlbCBjYXNvIGRlIG3Dumx0aXBsZXMgZ3J1cG9zLCBlbCAkXExhbWJkYSQtcGFyY2lhbCBwYXJhICR5X3IkIGFqdXN0YWRvcyBwYXJhIGxhcyBvdHJhcyAkcC0xJCB2YXJpYWJsZXMgZXN0w6EgZGFkbyBwb3IKJCRcTGFtYmRhKHlfcnx5XzEsLi4seV97ci0xfSx5e3IrMX0sLi4uLHlfcCkgPSBcZnJhY3tcTGFtYmRhX3B9e1xMYW1iZGFfe3AtMX19IFxzaW0gXExhbWJkYV97MSx2X0gsdl9FLXArMX0kJApkb25kZSAkXExhbWJkYV9wJCBlcyB1biAkXExhbWJkYSQgZGUgV2lsa2lucyBwYXJhIHRvZGFzIGxhcyAkcCQgdmFyaWFibGVzIHkgJFxMYW1iZGFfe3AtMX0kIGNvbnRpZW5lIGEgdG9kYXMgbGFzIHZhcmlhYmxlcyBleGNlcHR1YW5kbyBhICR5X3IkLiBFbCBjb3JyZXNwb25kaWVudGUgJEYkIGVzCiQkRj1cZnJhY3sxLVxMYW1iZGF9e1xMYW1iZGF9XGZyYWN7dl9FLXArMX17dl9IfSBcc2ltIEZfe3ZfSCx2X0UtcCsxfSQkCmRvbmRlICRcTGFtYmRhJCBlcyBkZWZpbmlkbyBlbiBsYSBlY3VhY2nDs24gYW50ZXJpb3IsICR2X0U9Ti1rJCB5ICR2X0g9ay0xJC5cXAoKTG9zIHZhbG9yZXMgJEYkLXBhcmNpYWxlcyB0YW50byBwYXJhIGVsIGNhc28gZGUgZG9zIGdydXBvcyBjb21vIHBhcmEgZWwgY2FzbyBkZSBtw7psdGlwbGVzIGdydXBvcyBubyBzb24gYXNvY2lhZG9zIGEgdW5hIHNpbXBsZSBkaW1lbnNpw7NuIGRlIGxhIHNlcGFyYWNpw7NuIGRlIGdydXBvcywgY29tbyBsb3Mgc29uIGxvcyBjb2VmaWNpZW50ZXMgZGUgbGEgZnVuY2nDs24gZGlzY3JpbWluYW50ZSBlc3RhbmRhcml6YWRhLiBFamVtcGxpZmljYW5kbyBsbyBhbnRlcmlvciwgJHlfciQgdGVuZHLDoSB1bmEgY29udHJpYnVjacOzbiBkaWZlcmVudGUgcGFyYSBjYWRhIHVuYSBkZSBsYXMgJHMkIGZ1bmNpb25lcyBkaXNjcmltaW5hbnRlcywgcGVybyBlbCAkRiQtcGFyY2lhbCBwYXJhIGVzdGEgdmFyaWFibGUgY29uc3RpdHV5ZSB1biDDrW5kaWNlIGdsb2JhbCBkZSBjb250cmlidWNpw7NuIGRlIGVsbGEgZW4gbGEgc2VwYXJhY2nDs24gZGUgbG9zIGdydXBvcywgYSB0cmF2w6lzIGRlIHRvZGFzIGxhcyBkaW1lbnNpb25lcy4gQSBwZXNhciBkZSBsbyBhbnRlcmlvciwgZ2VuZXJhbG1lbnRlIGxvcyB2YWxvcmVzIGRlICRGJC1wYXJjaWFsZXMgY2xhc2lmaWNhbiBsYXMgdmFyaWFibGVzIGVuIGVsIG1pc21vIG9yZGVuIHF1ZSBsbyBoYWNlbiBsb3MgY29lZmljaWVudGVzIGVzdGFuZGFyaXphZG9zIGRlIGxhIHByaW1lcmEgZnVuY2nDs24gZGlzY3JpbWluYW50ZSwgbcOhcyBhw7puIHNpIGxhIHByb3BvcmNpw7NuIHJlbGF0aXZhIGRlICRcbGFtYmRhXzEkIGVzIGdyYW5kZS5cXAoKVW4gw61uZGljZSBwYXJjaWFsIGRlIGFzb2NpYWNpw7NuIHBhcmEgJHlfciQgcHVlZGUgc2VyIGRlZmluaWRvIHBvciAKJCRSXjI9MS1cTGFtYmRhX3IgXHFxdWFkIHI9MSwyLC4uLixwJCQKZG9uZGUgJFxMYW1iZGFfciQgZXMgJFxMYW1iZGEkLXBhcmNpYWwgcGFyYSAkeV9yJC4gJFJeMiQgcGFyY2lhbCBlcyB1bmEgbWVkaWRhIGRlIGFzb2NpYWNpw7NuIGVudHJlIGxhcyB2YXJpYWJsZXMgZGUgYWdydXBhbWllbnRvIHkgJHlfaSQgZGVzcHXDqXMgZGUgYWp1c3RhciBsYXMgb3RyYXMgJChwLTEpJCAkeSRzLgoKIyMjIE90cm9zIG3DqXRvZG9zOiBDb3JyZWxhY2nDs24gZW50cmUgbGFzIHZhcmlhYmxlcyB5IGxhcyBmdW5jaW9uZXMgZGlzY3JpbWluYW50ZXMsIHkgUm90YWNpw7NuClxjaXRlcHtTbWl0aDE5OTd9IG1lbmNpb25hIHF1ZSBtdWNob3MgbGlicm9zIGUgaW52ZXN0aWdhY2lvbmVzIGRlc3RhY2FuIGEgbGEgY29ycmVsYWNpw7NuIGVudHJlIGxhcyB2YXJpYWJsZXMgeSBsYXMgZnVuY2lvbmVzIGRpc2NyaW1pbmFudGVzLCAkcl97eV9pLHpfan0kLCBjb21vIGxhIG1lam9yIG1hbmVyYSBkZSBtZWRpciBsYSBpbXBvcnRhbmNpYSBkZSBsYXMgdmFyaWFibGVzLiBTZSBkaWNlIHF1ZSBlc3TDoXMgY29ycmVsYWNpb25lcyBzb24gbcOhcyBpbmZvcm1hdGl2YXMgY29uIHJlc3BlY3RvIGEgbGEgY29udHJpYnVjacOzbiBjb25qdW50YSBkZSBsYXMgdmFyaWFibGVzIGEgbGFzIGZ1bmNpb25lcyBkaXNjcmltaW5hbnRlcyBxdWUgbG9zIGNvZWZpY2llbnRlcyBlc3RhbmRhcml6YWRvcy4gU2luIGVtYmFyZ28sIFxjaXRlcHtTbWl0aDE5OTd9IGNpdGEgYSBSZW5jaGVyIHF1acOpbiBoYSBtb3N0cmFkbyBxdWUgbGFzIGNvcnJlbGFjaW9uZXMgZW4gY3Vlc3Rpw7NuIG11ZXN0cmFuIGxhIGNvbnRyaWJ1Y2nDs24gZGUgY2FkYSB2YXJpYWJsZSBlbiB1biBjb250ZXh0byB1bml2YXJpYWRvIG1lam9yIHF1ZSBlbiB1biBtdWx0aXZhcmlhZG8uIEVuIGNvbnNlY3VlbmNpYSwgc29sbyBtdWVzdHJhIGPDs21vIGNhZGEgdmFyaWFibGUgZW4gc8OtIG1pc21hIHNlcGFyYSBsb3MgZ3J1cG9zLCBpZ25vcmFuZG8gbGEgcHJlc2VuY2lhIGRlIG90cmFzIHZhcmlhYmxlczsgZXMgYXPDrSBjb21vIG5vIG11ZXN0cmFuIGxhIGNvbnRyaWJ1Y2nDs24gY29uanVudGEgYSBsYSBzZXBhcmFjacOzbiBkZSBsb3MgZ3J1cG9zLiBFc3RlIG3DqXRvZG8gZXMgZW5nYcOxb3NvIGVuIGxhIGludGVycHJldGFjacOzbiBkZSBsYSBmdW5jacOzbiBkaXNjcmltaW5hbnRlLlxcCgpMYSByb3RhY2nDs24gZGUgbG9zIGNvZWZpY2llbnRlcyBkZSBsYSBmdW5jacOzbiBkaXNjcmltaW5hbnRlIGVzIHJlY29tZW5kYWJsZSBhIHZlY2VzLiBFc3RlIHByb2NlZGltaWVudG8gYnVzY2EgY29uc2VndWlyIHVuIHBhdHLDs24gY29uIGxvcyBjb2VmaWNpZW50ZXMgZW4gdmFsb3IgYWJzb2x1dG8gY2VyY2Fub3MgYSAwIG8gYSAxLiBBdW5xdWUgbGFzIGZ1bmNpb25lcyBkaXNjcmltaW5hbnRlcyBxdWVkYW4gbcOhcyBmw6FjaWxlcyBkZSBpbnRlcnByZXRhciwgc2UgcHJlc2VudGFuIGRvcyBwcm9ibGVtYXMgeSBlcyBxdWUgbG9zIGNvZWZpY2llbnRlcyBubyBtYXhpbWl6YW4gbGEgc2VwYXJhY2nDs24gZGUgbG9zIGdydXBvcyB5IHNvbiBjb3JyZWxhY2lvbmFkb3MuXFwKClxjaXRlcHtTbWl0aDE5OTd9IHJlY29taWVuZGEgcGFyYSBsYSBpbnRlcnByZXRhY2nDs24gZGUgbGFzIGZ1bmNpb25lcyBkaXNjcmltaW5hbnRlcyBlbCB1c28gZGUgbG9zIGNvZWZpY2llbnRlcyBlc3RhbmRhcml6YWRvcyBhbnRlcyBxdWUgZWwgZGUgY29ycmVsYWNpb25lcyBvIGVsIGRlIHJvdGFjacOzbi4KCiMjIEdyw6FmaWNhIGRlIGRpc3BlcnNpw7NuClVuYSBkZSBsYXMgdmVudGFqYXMgZGVsIEFuw6FsaXNpcyBEaXNjcmltaW5hbnRlIGVzIGxhIHJlZHVjY2nDs24gZGUgbGEgZGltZW5zaW9uYWxpZGFkLiBVc3VhbG1lbnRlLCBsYXMgcHJpbWVyYXMgZG9zIG8gdHJlcyBmdW5jaW9uZXMgZGlzY3JpbWluYW50ZXMgcG9zZWVuIGxhIG1heW9yIG1heGltaXphY2nDs24gZGUgbG9zIGdydXBvcywgaGFjaWVuZG8gcG9zaWJsZSBsYSByZWFsaXphY2nDs24gZGUgdW4gZ3LDoWZpY28gZGUgZGlzcGVyc2nDs24gZW4gMkQuIEFob3JhLCBzaSBsYSBkaW1lbnNpb25hbGlkYWQgZXNlbmNpYWwgZXMgbWF5b3IgYSBkb3MsIHNlIHZlcsOhIHBvc2libGVtZW50ZSB1biBzdXBlcnBvc2ljacOzbiBlbnRyZSBncnVwb3MuXFwKCkFsIGhhY2VyIGxhIGdyw6FmaWNhIHNlIGRlYmVuIGluY2x1aXIgdGFudG8gbG9zIHZhbG9yZXMgcGFyYSBsYXMgb2JzZXJ2YWNpb25lcyAkeV97aWp9JCBjb21vCiQkel97aWp9PVxsZWZ0KHpfezFpan0gXHF1YWQgel97MmlqfVxyaWdodCknPSBcbGVmdChhXzEneV97aWp9IFxxdWFkIGFfMid5X3tpan1ccmlnaHQpJz1BeV97aWp9IFxxcXVhZCBpPTEsLi4uLGs7aj0xLC4uLixuX2kkJAp5IGxvcyB2ZWN0b3JlcyBkZSBtZWRpYXMgdHJhbnNmb3JtYWRhcwokJFxiYXJ7el97aX19PVxsZWZ0KFxiYXJ7el97MWl9fSBccXVhZCBcYmFye3pfezJpfX1ccmlnaHQpJz1BXGJhcnt5X3tpfX0gXHFxdWFkIGk9MSwuLi4sayQkCgojIEV2YWx1YWNpw7NuIGRlIGxhIGV4YWN0aXR1ZCBkZSBsYSBjbGFzaWZpY2FjacOzbgpFdmFsdWFyIGxhIGV4YWN0aXR1ZCBkZSBsYSBjbGFzaWZpY2FjacOzbiwgbWVkaXIgbGFzIHRhc2FzIGRlIGFjaWVydG8sIGVzIHVuIG1lZGlvIGltcG9ydGFudGUgcGFyYSBkZXRlcm1pbmFyIGxhIHV0aWxpZGFkIGVzdGFkw61zdGljYSB5IHByw6FjdGljYSBkZSB1biBjb25qdW50byBkZSBmdW5jaW9uZXMuIEEgcHJpb3IgYSBsYSBjbGFzaWZpY2FjacOzbiBkZSBudWV2b3MgY2Fzb3MsIHNlIGRlYmUgdGVuZXIgdW4gaW5kaWNhZG9yIGRlIGxhIGV4YWN0aXR1ZCBkZSBsYXMgZnVuY2lvbmVzIGVuIGxhIGNsYXNpZmljYWNpw7NuIGRlIGxvcyBjYXNvcy4KClBhcmEgZWxsbywgc2UgY2xhc2lmaWNhbiBsb3MgY2Fzb3Mgb3JpZ2luYWxlcyB1c2FuZG8gbGFzIGZ1bmNpb25lcyB5IGV2YWx1YW5kbyBhIGV4YWN0aXR1ZCBkZSBsYXMgbWlzbWFzLiBQb3IgbWVkaW8gZGUgbGFzIGZ1bmNpb25lcyBkaXNjcmltaW5hbnRlcyBzZSBjYWxjdWxhbiBsb3MgcHVudGFqZXMgcGFyYSBjYWRhIGNhc28geSBzZSBjb21wYXJhIGxhIHViaWNhY2nDs24gb2J0ZW5pZGEgcG9yIG1lZGlvIGRlIGVzdG9zIGNvbiBsYSByZWFsLiBGaW5hbG1lbnRlLCBzZSBjYWxjdWxhIHVuIHBvcmNlbnRhamUgZGUgYXNpZ25hY2lvbmVzIGNvcnJlY3Rhcy4KCiMjIFZhbGlkYWNpw7NuIGNydXphZGEKRXMgZGUgdml0YWwgaW1wb3J0YW5jaWEgcmVhbGl6YXIgdmFsaWRhY2nDs24gY3J1emFkYSwgZW4gZXNwZWNpYWwgc2kgc2UgcXVpZXJlbiBhcGxpY2FyIHByb2NlZGltaWVudG9zIGNsYXNpZmljYXRvcmlvcyBwb3N0ZXJpb3JtZW50ZS4gU2UgcHJlc2VudGFyw6FuIGRvcyB0aXBvcyBkZSB2YWxpZGFjacOzbiBjcnV6YWRhLgoqICoqTcOpdG9kbyBqYWNra25pZmUuKiogU2UgY29ub2NlIHRhbWJpw6luIGNvbW8gZWwgbcOpdG9kbyBkZSB1bm8gYSBmdWVyYS4gVW4gY2FzbyBkZSBsYSBtdWVzdHJhIGVzIHNpc3RlbcOhdGljYW1lbnRlIGRlamFkbyBmdWVyYSBkZWwgYW7DoWxpc2lzIGRpc2NyaW1pbmFudGUuIEx1ZWdvLCBlbCBjYXNvIGV4Y2x1aWRvIGVzIGNsYXNpZmljYWRvIGVuIHVubyBkZSBsb3MgZ3J1cG9zIHV0aWxpemFuZG8gbGFzIGZ1bmNpb25lcyBkaXNjcmltaW5hbnRlcyBnZW5lcmFkYXMgZW4gZWwgYW7DoWxpc2lzLiBFc3RlIHByb2NlZGltaWVudG8gc2UgcmVwaXRlIHBhcmEgdG9kb3MgbG9zIGNhc29zLiBMYSBpbmZvcm1hY2nDs24gb2J0ZW5pZGEgc2lydmUgcGFyYSBkZXRlcm1pbmFyIGxhIGVzdGFiaWxpZGFkIGRlIGxvcyBlc3RhZMOtc3RpY29zIG9idGVuaWRvcy4KKiAqKk3DqXRvZG8gaG9sZC1vdXQuKiogUGFyYSBlc3RlIHByb2NlZGltaWVudG8gbGEgbXVlc3RyYSBlcyBkaXZpZGlkYSBlbiB0cmVzIHBhcnRlczogZG9zIHRlcmNpb3Mgc29uIHV0aWxpemFkb3MgcGFyYSByZWFsaXphciBlbCBhbsOhbGlzaXMgZGlzY3JpbWluYW50ZXMgeSBlbCB0ZXJjaW8gcmVzdGFudGUgcGFyYSByZWFsaXphciB2YWxpZGFjacOzbiBjcnV6YWRhLiBMYSBleGFjdGl0dWQgZGUgbGEgY2xhc2lmaWNhY2nDs24gcGFyYSBsYSBtdWVzdHJhIG3DoXMgcGVxdWXDsWEgaW5kaWNhIGxhIHRhc2EgZGUgYWNpZXJ0byBxdWUgc2UgcHVlZGUgZXNwZXJhciBsb2dyYXIgcGFyYSBudWV2YXMgbXVlc3RyYXMuCgojIyMgUHJvcG9yY2nDs24gZGUgb3BvcnR1bmlkYWQgeSB0YXNhcyBkZSBhY2llcnRvClNlIHB1ZWRlIGV2YWx1YXIgZWwgYWNpZXJ0byBvIGV4YWN0aXR1ZCBkZSBsYSBjbGFzaWZpY2FjacOzbiBjb21wYXJhbmRvIGxhIHByb3BvcmNpw7NuIGRlIGNhc29zIGNvcnJlY3RhbWVudGUgY2xhc2lmaWNhZG9zIGZyZW50ZSBhIGxhIHRhc2EgZGUgYmFzZSBvIGRlIGFjaWVydG8gdXNhbmRvIHVuIHRlc3QgZGUgcHJvcG9yY2lvbmVzICR6JC4gTGEgdGFzYSBkZSBiYXNlIGVzIGxhIHByb3BvcmNpw7NuIGRlIGNhc29zIHF1ZSBzZSBlc3BlcmFuIHNlYW4gY29ycmVjdGFtZW50ZSBjbGFzaWZpY2Fkb3MgZW4gbGFzIGJhc2VzIGRlIGxhIG1lam9yIGVzdHJhdGVnaWEgYWx0ZXJuYXRpdmEgZGlzcG9uaWJsZS4gTGEgc2lnbmlmaWNhbmNpYSBkZSBsb3MgcmVzdWx0YWRvcyBkZSBjbGFzaWZpY2FjacOzbiBwb3IgY29tcGFyYWNpw7NuIGRlIGxhIHByb3BvcmNpw7NuIGRlIGxhcyBwcmVkaWNjaW9uZXMgY29ycmVjdGFzIGZyZW50ZSBhIGxhIHByb3BvcmNpw7NuIGVzcGVyYWRhIGVuIGxhIGJhc2UgZGUgb3BvcnR1bmlkYWQuCgpDdWFuZG8gbG9zIGdydXBvcyBlbiBsb3MgcXVlIGxvcyBjYXNvcyBmdWVyb24gY2xhc2lmaWNhZG9zIHNvbiBkZSBpZ3VhbCB0YW1hw7FvLCBsYSBwcm9wb3JjacOzbiBkZSBjbGFzaWZpY2FjaW9uZXMgY29ycmVjdGFzIHF1ZSBzZSBlc3BlcmFuIG9jdXJyYW4gZW4gbGEgYmFzZSBkZSBvcG9ydHVuaWRhZCBlcyAkXGZyYWN7MX17a30kIGRvbmRlICRrJCBlcyBlbCBuw7ptZXJvIGRlIGdydXBvczsgY2FkYSBjYXNvIHRpZW5lIGxhIG1pc21hIHByb2JhYmlsaWRhZCBkZSBzZXIgY2xhc2lmaWNhZG8gZW4gY2FkYSB1bm8gZGUgbG9zIGdydXBvcy4gU2luIGVtYmFyZ28sIGN1YW5kbyBubyB0aWVuZW4gaWd1YWwgdGFtYcOxbyBleGlzdGVuIGRvcyBlc3RyYXRlZ2lhcyBwYXJhIGNhbGN1bGFyIGxhcyBwcm9wb3JjaW9uZXMgZGUgb3BvcnR1bmlkYWQuIFNpIHNlIGVzdMOhIGludGVyZXNhZG8gZW4gbWF4aW1pemFyIGVsIMOpeGl0byBzb2JyZSB0b2RhcyBsYXMgY2xhc2lmaWNhY2lvbmVzLCBzZSBjbGFzaWZpY2FuIGEgdG9kb3MgbG9zIGNhc29zIGVuIGVsIGdydXBvIGRlIG1heW9yIHRhbWHDsW8geSBsYSB0YXNhIGRlIG9wb3J0dW5pZGFkIGVzIGNhbGN1bGFkYSB1c2FuZG8gJFxmcmFje259e059JCBkb25kZSAkbiQgZXMgZWwgbsO6bWVybyBkZSBjYXNvcyBlbiBlbCBncnVwbyBkZSBtYXlvciB0YW1hw7FvIHkgJE4kIGxhIHRvdGFsaWRhZCBkZSBjYXNvcyBlbiB0b2RvcyBsb3MgZ3J1cG9zLiBQZXJvIHNpIHNlIGVzdMOhIGludGVyZXNhZG8gZW4gbcOhcyBxdWUgbWF4aW1pemFyIGVsIMOpeGl0byBlbiBsYXMgY2xhc2lmaWNhY2lvbmVzLCBsYSBmw7NybXVsYSBwYXJhIGxhIHByb2JhYmlsaWRhZCBkZSBvcG9ydHVuaWRhZCBlcyAkcF8xYV8xK3BfMmFfMisuLi4rcF9rYV9rJCBjb24gJHAkIGxhIHByb3BvcmNpw7NuIGRlIGxhIHRvdGFsaWRhZCBkZSBsYSBtdWVzdHJhIHBlcnRlbmVjaWVudGUgYSBlc2UgZ3J1cG8sICRhJCBsYSBwcm9wb3JjacOzbiBhY3R1YWwgZGUgY2Fzb3MgY2xhc2lmaWNhZG9zIHBvciBhbsOhbGlzaXMgZGlzY3JpbWluYW50ZSBlbiB1biBncnVwbyBwYXJ0aWN1bGFyIHkgJGskIGVsIG7Dum1lcm8gZGUgZ3J1cG9zLgoKTGEgdGFzYSBkZSBvcG9ydHVuaWRhZCBwdWVkZSBzZXIgY29udHJhc3RhZGEgY29uIGxhIHByb3BvcmNpw7NuIGRlIGNsYXNpZmljYWNpb25lcyBjb3JyZWN0YXMgdXNhbmRvIGVsIHRlc3QgJHokCiQkej1cZnJhY3tOcF9hLU5wX2N9e05wX2MoMS1wX2MpfSQkCmRvbmRlICROJCBlcyBsYSB0b3RhbGlkYWQgZGUgbGEgbXVlc3RyYSwgJHBfYSQgbGEgcHJvcG9yY2nDs24gZGUgY2Fzb3MgY29ycmVjdGFtZW50ZSBjbGFzaWZpY2Fkb3MgbWVkaWFudGUgYW7DoWxpc2lzIGRpc2NyaW1pbmFudGUgeSAkcF9jJCBsYSBwcm9wb3JjacOzbiBkZSBjYXNvcyBlc3BlcmFkb3Mgc2VyIGNsYXNpZmljYWRvcyBjb3JyZWN0YW1lbnRlIGVuIGxhIGJhc2UgZGUgb3BvcnR1bmlkYWQuIEVzdGUgdmFsb3IgZGUgJHokIHNlIGNvbXBhcmEgY29uIHVuIHZhbG9yICR6JCBkZSB1bmEgY29sYSwgZXN0ZSDDumx0aW1vICR6JCBkZXBlbmRlIGRlIHVuICRcYWxwaGEkLgoKIyBBbsOhbGlzaXMgRGlzY3JpbWluYW50ZSBQcmVkaWN0aXZvClBhcmEgcmVhbGl6YXIgdW4gYW7DoWxpc2lzIGRpc2NyaW1pbmFudGUgcHJlZGljdGl2bywgc2UgZGViZW4gdGVuZXIgZW4gY3VlbnRhIGxhcyByZWdsYXMgZGUgY2xhc2lmaWNhY2nDs24gLWZ1bmNpb25lcyBkaXNjcmltaW5hbnRlcy0gb2J0ZW5pZGFzIGRlIHVuIGFuw6FsaXNpcyBkaXNjcmltaW5hbnRlIGRlc2NyaXB0aXZvIHByZXZpbywgeSBzZWfDum4gZXN0YXMgcmVnbGFzIHNlIGxlIGFzaWduYSBhIGxvcyBudWV2b3MgaW5kaXZpZHVvcyBhIHVuIGdydXBvLgoKRXMgaW1wb3J0YW50ZSByZXNhbHRhciBxdWUgbGFzIGZ1bmNpb25lcyBjbGFzaWZpY2Fkb3JhcyBzZSBkaWZlcmVuY2lhbiBkZSBsYXMgZnVuY2lvbmVzIGRpc2NyaW1pbmFudGVzIHF1ZSBzZSBoYW4gdmVuaWRvIHV0aWxpemFuZG8uIEVzdG8gc2UgZGViZSBhIHF1ZSBsYXMgZnVuY2lvbmVzIGRpc2NyaW1pbmFudGVzIG5vIHNvbiBlc3BlY8SxzIFmaWNhcyBwYXJhIGdydXBvcywgc29sbyBzZSBkZXRlcm1pbmEgdW4gcHVudGFqZSB5IHNlIGFzaWduYSBhbCBncnVwbyBxdWUgc2UgY29uc2lkZXJlIG3DoXMgYWRlY3VhZG8gbyBhbCBxdWUgdGVuZ2EgbWF5b3IgcHJvYmFiaWxpZGFkIGRlIHBlcm1hbmVjZXIuIEVuIGNvbnRyYXN0ZSwgdXNhbmRvIGxhcyBmdW5jaW9uZXMgY2xhc2lmaWNhZG9yYXMsIGNhZGEgbnVldm8gY2FzbyByZWNpYmlyw6EgdW5hIHB1bnRhamUgZGlzY3JpbWluYWRvciBlbiBjYWRhIGZ1bmNpw7NuIGVzcGVjxLHMgWZpY2EgZGUgZ3J1cG8uCgpPdHJhIGZvcm1hIGRlIGhhY2VyIGVsIGFuw6FsaXNpcyBwcmVkaWN0aXZvIGVzIG1lZGlhbnRlIGVsIHVzbyBkZSBmdW5jaW9uZXMgZGlzY3JpbWluYW50ZXMgZGVyaXZhZGFzCmRlIGNhc29zIGRlIHVuYSBtdWVzdHJhIGVuIHVuIGFuw6FsaXNpcyBkZXNjcmlwdGl2bywgcXVpZW5lcyBoYW4gc2lkbyBwcmVhc2lnbmFkYXMgYSB1biBjb25qdW50byBkZSBncnVwb3MsIHBhcmEgY2xhc2lmaWNhciBtdWVzdHJhcyBkZSBjYXNvcyBubyBhc2lnbmFkb3MgZGUgb3Ryb3MgZ3J1cG9zIGEgZWxsYXMuIEVzdGUgbcOpdG9kbyB1dGlsaXphIHVuIGNvbmp1bnRvIGRlIGNvZWZpY2llbnRlcyBsbGFtYWRvcyBjb2VmaWNpZW50ZXMgZGUgZnVuY2lvbmVzIGxpbmVhbGVzIGRpc2NyaW1pbmFudGVzIGRlIEZpc2hlciBvIGRlIGZ1bmNpb25lcyBjbGFzaWZpY2Fkb3JhcywgcXVlIHNvbiBnZW5lcmFkYXMgcGFyYSBjYWRhIGdydXBvIHBvciBhbsOhbGlzaXMgZGlzY3JpbWluYW50ZXMuCgojIEltcGxlbWVudGFjaW9uZXMKCkVzdGUgdHJhYmFqbyB1dGlsaXphIGxhIGZhbW9zYSBiYXNlIGRlIGRhdG9zICJpcmlzIiwgZXN0YSBjb250aWVuZSBtZWRpY2lvbmVzIGRlIDE1MCBmbG9yZXMgaXJpcyBkZSB0cmVzIGVzcGVjaWVzIGRpZmVyZW50ZXMuCgpMYXMgdHJlcyBjbGFzZXMgZGVsIGNvbmp1bnRvIGRlIGRhdG9zIElyaXM6CgpJcmlzLXNldG9zYSAobiA9IDUwKQpJcmlzLXZlcnNpY29sb3IgKG4gPSA1MCkKSXJpcy12aXJnaW5pY2EgKG4gPSA1MCkKTGFzIGN1YXRybyBjYXJhY3RlcsOtc3RpY2FzIGRlbCBjb25qdW50byBkZSBkYXRvcyBJcmlzOgoKTG9uZ2l0dWQgZGVsIHNlcGFsIGVuIGNtCkFuY2hvIHNlcGFsIGVuIGNtCkxvbmdpdHVkIGRlbCBww6l0YWxvIGVuIGNtCkFuY2h1cmEgZGVsIHDDqXRhbG8gZW4gY20KCiFbXShodHRwczovL3NlYmFzdGlhbnJhc2Noa2EuY29tL2ltYWdlcy9ibG9nLzIwMTQvbGluZWFyLWRpc2NyaW1pbmFudC1hbmFseXNpcy9pcmlzX3BldGFsX3NlcGFsLnBuZykKCmBgYHtyfQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZ3JpZCkKbGlicmFyeShncmlkRXh0cmEpCmhlYWQoaXJpcykKYGBgCgpQYXJhIG9idGVuZXIgdW5hIGlkZWEgYXByb3hpbWFkYSBkZSBjw7NtbyBzZSBkaXN0cmlidXllbiBsYXMgbXVlc3RyYXMgZGUgbGFzIHRyZXMgY2xhc2VzLCBzZSB2aXN1YWxpemFyw6EgbGFzIGRpc3RyaWJ1Y2lvbmVzIGRlIGxhcyBjdWF0cm8gY2FyYWN0ZXLDrXN0aWNhcyBkaWZlcmVudGVzIGVuIGhpc3RvZ3JhbWFzIHVuaWRpbWVuc2lvbmFsZXMuCgpgYGB7cn0KZGF0IDwtIGRhdGEuZnJhbWUoaXJpcyRTZXBhbC5MZW5ndGgsIGlyaXMkU3BlY2llcykKU0wgPC1nZ3Bsb3QoZGF0LGFlcyh4PWlyaXMuU2VwYWwuTGVuZ3RoKSkgKyAKICAgICBnZW9tX2hpc3RvZ3JhbShkYXRhPXN1YnNldChkYXQsaXJpcy5TcGVjaWVzID09ICJzZXRvc2EiKSxmaWxsID0gInJlZCIsIGFscGhhID0gMC40LCBiaW5zID0gNTApICsKICAgICBnZW9tX2hpc3RvZ3JhbShkYXRhPXN1YnNldChkYXQsaXJpcy5TcGVjaWVzID09ICJ2ZXJzaWNvbG9yIiksZmlsbCA9ICJibHVlIiwgYWxwaGEgPSAwLjQsIGJpbnMgPSA1MCkgKwogICAgIGdlb21faGlzdG9ncmFtKGRhdGE9c3Vic2V0KGRhdCxpcmlzLlNwZWNpZXMgPT0gInZpcmdpbmljYSIpLGZpbGwgPSAiZ3JlZW4iLCBhbHBoYSA9IDAuNCwgYmlucyA9IDUwKQoKZGF0IDwtIGRhdGEuZnJhbWUoaXJpcyRTZXBhbC5XaWR0aCwgaXJpcyRTcGVjaWVzKQpTVyA8LWdncGxvdChkYXQsYWVzKHg9aXJpcy5TZXBhbC5XaWR0aCkpICsgCiAgICAgZ2VvbV9oaXN0b2dyYW0oZGF0YT1zdWJzZXQoZGF0LGlyaXMuU3BlY2llcyA9PSAic2V0b3NhIiksZmlsbCA9ICJyZWQiLCBhbHBoYSA9IDAuMiwgYmlucyA9IDUwKSArCiAgICAgZ2VvbV9oaXN0b2dyYW0oZGF0YT1zdWJzZXQoZGF0LGlyaXMuU3BlY2llcyA9PSAidmVyc2ljb2xvciIpLGZpbGwgPSAiYmx1ZSIsIGFscGhhID0gMC4yLCBiaW5zID0gNTApICsKICAgICBnZW9tX2hpc3RvZ3JhbShkYXRhPXN1YnNldChkYXQsaXJpcy5TcGVjaWVzID09ICJ2aXJnaW5pY2EiKSxmaWxsID0gImdyZWVuIiwgYWxwaGEgPSAwLjIsIGJpbnMgPSA1MCkKCmRhdCA8LSBkYXRhLmZyYW1lKGlyaXMkUGV0YWwuTGVuZ3RoLCBpcmlzJFNwZWNpZXMpClBMIDwtZ2dwbG90KGRhdCxhZXMoeD1pcmlzLlBldGFsLkxlbmd0aCkpICsgCiAgICAgZ2VvbV9oaXN0b2dyYW0oZGF0YT1zdWJzZXQoZGF0LGlyaXMuU3BlY2llcyA9PSAic2V0b3NhIiksZmlsbCA9ICJyZWQiLCBhbHBoYSA9IDAuNCwgYmlucyA9IDUwKSArCiAgICAgZ2VvbV9oaXN0b2dyYW0oZGF0YT1zdWJzZXQoZGF0LGlyaXMuU3BlY2llcyA9PSAidmVyc2ljb2xvciIpLGZpbGwgPSAiYmx1ZSIsIGFscGhhID0gMC40LCBiaW5zID0gNTApICsKICAgICBnZW9tX2hpc3RvZ3JhbShkYXRhPXN1YnNldChkYXQsaXJpcy5TcGVjaWVzID09ICJ2aXJnaW5pY2EiKSxmaWxsID0gImdyZWVuIiwgYWxwaGEgPSAwLjQsIGJpbnMgPSA1MCkKCmRhdCA8LSBkYXRhLmZyYW1lKGlyaXMkUGV0YWwuV2lkdGgsIGlyaXMkU3BlY2llcykKUFcgPC1nZ3Bsb3QoZGF0LGFlcyh4PWlyaXMuUGV0YWwuV2lkdGgpKSArIAogICAgIGdlb21faGlzdG9ncmFtKGRhdGE9c3Vic2V0KGRhdCxpcmlzLlNwZWNpZXMgPT0gInNldG9zYSIpLGZpbGwgPSAicmVkIiwgYWxwaGEgPSAwLjQsIGJpbnMgPSA1MCkgKwogICAgIGdlb21faGlzdG9ncmFtKGRhdGE9c3Vic2V0KGRhdCxpcmlzLlNwZWNpZXMgPT0gInZlcnNpY29sb3IiKSxmaWxsID0gImJsdWUiLCBhbHBoYSA9IDAuNCwgYmlucyA9IDUwKSArCiAgICAgZ2VvbV9oaXN0b2dyYW0oZGF0YT1zdWJzZXQoZGF0LGlyaXMuU3BlY2llcyA9PSAidmlyZ2luaWNhIiksZmlsbCA9ICJncmVlbiIsIGFscGhhID0gMC40LCBiaW5zID0gNTApCmBgYAoKYGBge3J9CiMgU2V0b3NhIC0+IFJlZAojIFZlcnNpY29sb3IgLT4gQmx1ZQojIFZpcmdpbmljYSAtPiBHcmVlbgpncmlkLmFycmFuZ2UoU0wsIFNXLFBMLCBQVywgbmNvbCA9IDIpCmBgYAoKU29sbyBhbCBtaXJhciBlbCBoaXN0b2dyYW1hIGVzIGZhY2lsIGRldGVjdGFyIHF1ZSBsYXMgY2FyYWN0ZXJpc3RpY2FzIGFzb2NpYWRvcyBhIGxvcyBwZXRhbG9zIHNvbiBtYXMgdXRpbGVzIGFsIG1vbWVudG8gZGUgZGlzdGFuY2lhciBsYXMgY2FyYWN0ZXJpc3RpY2EuCgojIEFuYWxpc2lzIGRpc2NyaW1pbmFudGUgbGluZWFsIGVuIDUgcGFzb3MKRW4gbGEgcHJhY3RpY2EsIGFudGVzIGRlIGVtcGV6YXIgcmVhbG1lbnRlIGEgaGFjZXIgbG9zIHNpZ3VpZW50ZXMgcGFzb3Mgc2UgZGViZSB0ZW5lciBsb3MgZGF0b3MgcHJlcHJvY2VzYWRvcyBoc2F0YSBlbCBwdW50byBkZSBlc3RhciBsaXN0b3MgcGFyYSBlbCBhbmFsaXNpcyBkaXNjaW1pbmFudGUKCiMjIFBhc28gMTogQ2FsY3VsYXIgbG9zIHZlY3RvcmVzIGRlIG1lZGlhCgpFbiBlc3RlIHByaW1lciBwYXNvLCBjb21lbnphcmVtb3MgY29uIHVuIGPDoWxjdWxvIHNpbXBsZSBkZSBsb3MgdmVjdG9yZXMgbWVkaW9zICRtX2ksIChpID0gMSwyLDMpJCBkZSBsYXMgMyBkaWZlcmVudGVzIGNsYXNlcwoKYGBge3J9CmNsYXNzZXMgPC0gc3BsaXQoaXJpc1ssMTo0XSwgZiA9IGlyaXNbLDVdKQptZWFuIDwtIGxhcHBseShjbGFzc2VzLGNvbE1lYW5zKQptZWFuCmBgYAoKIyMgUGFzbyAyOiBDYWxjdWxhciBsYXMgbWF0cmljZXMgZGUgZGlzcGVyY2lvbiAKCkFob3JhLCB2YW1vcyBhIGNhbGN1bGFyIGxhcyBkb3MgbWF0cmljZXMgNHg0LWRpbWVuc2lvbmFsOiBMYSBkZW50cm8gZGUgbGEgY2xhc2UgeSBsYSBtYXRyaXogZGUgZGlzcGVyc2nDs24gZW50cmUgY2xhc2VzLgoKIyMjIDIuMSBtYXRyaXogZGUgZGlzcGVyY2lvbiBkZW50cm8gZGUgbGFzIGNsYXNlcwoKJCQgU193ID0gXHN1bV97aT0xfV5jIFNfaSQkCgpkb25kZSAKCiQkIFNfaSA9IFxzdW1fe3hcaW4gRF9pfV5uICh4LW1faSkoeC1tX2kpJyA9IFxzdW1fe2k9MX1eYyhOX2kgLSAxKVxTaWdtYV9pJCQKCiMjIyAyLjIgbWF0cml6IGRlIGRpc3BlcmNpb24gZW50cmUgY2xhc2VzCgokJCBTX2IgPSBcc3VtX3tpPTF9XmMgTl9pIChtX2kgLSBtKShtX2kgLSBtKScgJCQKCmBgYHtyfQpTdyA8LSBSZWR1Y2UoIisiLGxhcHBseShjbGFzc2VzLHZhcikpKjQ5ClN3CmBgYAoKYGBge3J9Cm0gPC0gY29sTWVhbnMoaXJpc1ssMTo0XSkKU2IgPC1SZWR1Y2UoIisiLGxhcHBseShsYXBwbHkobWVhbixmdW5jdGlvbih4KShgLWAoeCxtKSkpLHRjcm9zc3Byb2QpKSo1MApTYgpgYGAKCmBgYHtyfQplaSA8LSBlaWdlbihzb2x2ZShTdyklKiVTYikKZWkKYGBgCgp0YW50byBsb3MgYXV0b3ZlY3RvcmVzIGNvbW8gbG9zIHZhbG9yZXMgcHJvcGlvcyBub3MgcHJvcG9yY2lvbmFuIGluZm9ybWFjacOzbiBzb2JyZSBsYSBkaXN0b3JzacOzbiBkZSB1bmEgdHJhbnNmb3JtYWNpw7NuIGxpbmVhbDogTG9zIHZlY3RvcmVzIHByb3Bpb3Mgc29uIGLDoXNpY2FtZW50ZSBsYSBkaXJlY2Npw7NuIGRlIGVzdGEgZGlzdG9yc2nDs24geSBsb3MgdmFsb3JlcyBwcm9waW9zIHNvbiBlbCBmYWN0b3IgZGUgZXNjYWxhIHBhcmEgTG9zIHZlY3RvcmVzIHByb3Bpb3MgcXVlIGRlc2NyaWJlbiBsYSBtYWduaXR1ZCBkZSBsYSBkaXN0b3JzacOzbi4KClNpIGVzdGFtb3MgcmVhbGl6YW5kbyBlbCBMREEgcGFyYSBsYSByZWR1Y2Npw7NuIGRlIGxhIGRpbWVuc2lvbmFsaWRhZCwgbG9zIHZlY3RvcmVzIHByb3Bpb3Mgc29uIGltcG9ydGFudGVzIHlhIHF1ZSBmb3JtYXLDoW4gbG9zIG51ZXZvcyBlamVzIGRlIG51ZXN0cm8gbnVldm8gc3ViZXNwYWNpbyBjYXJhY3RlcsOtc3RpY2E7IExvcyB2YWxvcmVzIHByb3Bpb3MgYXNvY2lhZG9zIHNvbiBkZSBwYXJ0aWN1bGFyIGludGVyw6lzIHlhIHF1ZSBub3MgZGlyw6FuIGPDs21vIHNvbiBkZSAiaW5mb3JtYXRpdm9zIiBsb3MgbnVldm9zICJlamVzIi4KCmBgYHtyfQplaSR2YWx1ZXMvc3VtKGVpJHZhbHVlcykKYGBgCgpgYGB7cn0KVyA8LSBlaSR2ZWN0b3JzWywxOjJdClcKYGBgCgpgYGB7cn0KWSA8LSBkYXRhLm1hdHJpeChpcmlzWywxOjRdKSUqJVcKZGF0IDwtIGRhdGEuZnJhbWUoWSwgaXJpcyRTcGVjaWVzKQpnZ3Bsb3QoZGF0KSArIGdlb21fcG9pbnQoYWVzKFgxLCBYMiwgY29sb3VyID0gaXJpcy5TcGVjaWVzLCBzaGFwZSA9IGlyaXMuU3BlY2llcyksIHNpemUgPSAyLjUpCmBgYAoKIyBMREEgdmlhIE1BU1MKCmBgYHtyfQpsaWJyYXJ5KE1BU1MpCnIgPC0gbGRhKGZvcm11bGEgPSBTcGVjaWVzIH4gLiwgCiAgICAgICAgIGRhdGEgPSBpcmlzLCAKICAgICAgICAgcHJpb3IgPSBjKDEsMSwxKS8zKQpgYGAKCmBgYHtyfQpyJHByaW9yCnIkY291bnRzCnIkbWVhbnMKciRzY2FsaW5nCnIkc3ZkCmBgYAoKYGBge3J9CnByb3AgPC0gciRzdmReMi9zdW0ociRzdmReMikKcHJvcApgYGAKCmBgYHtyfQpyMiA8LSBsZGEoZm9ybXVsYSA9IFNwZWNpZXMgfiAuLCAKICAgICAgICAgIGRhdGEgPSBpcmlzLCAKICAgICAgICAgIHByaW9yID0gYygxLDEsMSkvMywKICAgICAgICAgIENWID0gVFJVRSkKaGVhZChyMiRjbGFzcykKaGVhZChyMiRwb3N0ZXJpb3IsIDMpCmBgYAoKYGBge3J9CnRyYWluIDwtIHNhbXBsZSgxOjE1MCwgNzUpCiAKcjMgPC0gbGRhKFNwZWNpZXMgfiAuLCAjIHRyYWluaW5nIG1vZGVsCiAgICAgICAgIGlyaXMsIAogICAgICAgICBwcmlvciA9IGMoMSwxLDEpLzMsIAogICAgICAgICBzdWJzZXQgPSB0cmFpbikKIApwbGRhID0gcHJlZGljdChvYmplY3QgPSByLCAjIHByZWRpY3Rpb25zCiAgICAgICAgICAgICAgIG5ld2RhdGEgPSBpcmlzWy10cmFpbiwgXSkKIApoZWFkKHBsZGEkY2xhc3MpICMgY2xhc3NpZmljYXRpb24gcmVzdWx0CmhlYWQocGxkYSRwb3N0ZXJpb3IsIDMpICMgcG9zdGVyaW9yIHByb2IuCmhlYWQocGxkYSR4LCAzKSAjIExEIHByb2plY3Rpb24KYGBgCgpgYGB7cn0KbGlicmFyeShNQVNTKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoc2NhbGVzKQpsaWJyYXJ5KGdyaWRFeHRyYSkKCnBjYSA8LSBwcmNvbXAoaXJpc1ssLTVdLAogICAgICAgICAgICAgIGNlbnRlciA9IFRSVUUsCiAgICAgICAgICAgICAgc2NhbGUuID0gVFJVRSkgCgpwcm9wLnBjYSA9IHBjYSRzZGV2XjIvc3VtKHBjYSRzZGV2XjIpCgpsZGEgPC0gbGRhKFNwZWNpZXMgfiAuLCAKICAgICAgICAgICBpcmlzLCAKICAgICAgICAgICBwcmlvciA9IGMoMSwxLDEpLzMpCgpwcm9wLmxkYSA9IHIkc3ZkXjIvc3VtKHIkc3ZkXjIpCgpwbGRhIDwtIHByZWRpY3Qob2JqZWN0ID0gbGRhLAogICAgICAgICAgICAgICAgbmV3ZGF0YSA9IGlyaXMpCmRhdGFzZXQgPSBkYXRhLmZyYW1lKHNwZWNpZXMgPSBpcmlzWywiU3BlY2llcyJdLAogICAgICAgICAgICAgICAgICAgICBwY2EgPSBwY2EkeCwgbGRhID0gcGxkYSR4KQoKcDEgPC0gZ2dwbG90KGRhdGFzZXQpICsgZ2VvbV9wb2ludChhZXMobGRhLkxEMSwgbGRhLkxEMiwgY29sb3VyID0gc3BlY2llcywgc2hhcGUgPSBzcGVjaWVzKSwgc2l6ZSA9IDIuNSkgKyAKICBsYWJzKHggPSBwYXN0ZSgiTEQxICgiLCBwZXJjZW50KHByb3AubGRhWzFdKSwgIikiLCBzZXA9IiIpLAogICAgICAgeSA9IHBhc3RlKCJMRDIgKCIsIHBlcmNlbnQocHJvcC5sZGFbMl0pLCAiKSIsIHNlcD0iIikpCgpwMiA8LSBnZ3Bsb3QoZGF0YXNldCkgKyBnZW9tX3BvaW50KGFlcyhwY2EuUEMxLCBwY2EuUEMyLCBjb2xvdXIgPSBzcGVjaWVzLCBzaGFwZSA9IHNwZWNpZXMpLCBzaXplID0gMi41KSArCiAgbGFicyh4ID0gcGFzdGUoIlBDMSAoIiwgcGVyY2VudChwcm9wLnBjYVsxXSksICIpIiwgc2VwPSIiKSwKICAgICAgIHkgPSBwYXN0ZSgiUEMyICgiLCBwZXJjZW50KHByb3AucGNhWzJdKSwgIikiLCBzZXA9IiIpKQoKZ3JpZC5hcnJhbmdlKHAxLCBwMikKYGBgCgojIyBMREEgZm9yIENsYXNzaWZpY2F0aW9uCgpgYGB7cn0KbGlicmFyeShNQVNTKQoKWCA8LSBtYXRyaXgoYygyLDMsMyw0LDQsNSw1LDYsNSw3LDIsMSwzLDIsNCwyLDQsMyw2LDQsNyw2LDEsNiwyLDcsMSw3KSwgbmNvbD0yLCBieXJvdz1UKQpZIDwtIGMoMSwxLDEsMSwxLDIsMiwyLDIsMiwyLDMsMywzKTsKcGxvdChYLGNvbD1ZLCBwY2g9MTkpCmBgYAoKYGBge3J9CmlkeF9jbGFzczEgPC0gd2hpY2goWT09MSkgICMgaW5kZXhlcyBvZiB0aGUgb2JzZXJ2YXRpb25zIGZvciBlYWNoIGNsYXNzCmlkeF9jbGFzczIgPC0gd2hpY2goWT09MikKCm15LmRhdGEgPC0gZGF0YS5mcmFtZShYMT1YWywxXSwgWDI9WFssMl0sIFk9WSkKbW9kZWwgPC0gbGRhKFkgfiAuICwgZGF0YT1teS5kYXRhKQptb2RlbApgYGAKCmBgYHtyfQpwcm9qZWN0ZWQuZGF0YSA8LSBYICUqJSBtb2RlbCRzY2FsaW5nCnBsb3QocHJvamVjdGVkLmRhdGEsIGNvbD1ZLCBwY2g9MTkpCmBgYAoKYGBge3J9CiMgbm93IGxldCdzIHByZWRpY3QgdGhlIGNsYXNzIG9mIGEgbmV3IHNhbXBsZQpuZXcuZGF0YSA8LSBkYXRhLmZyYW1lKFgxPWMoMiksIFgyPWMoNS4xKSkgIyBuZXcgZGF0YSBwb2ludCBzaG91bGQgYmUgY2xhc3MgMwpwcmVkaWN0aW9uIDwtIHByZWRpY3QobW9kZWwsIG5ldy5kYXRhKQptb2RlbCRwcmlvciAgICAgICAgICAjIHRoZSBwcmlvciBwcm9iYWJpbGl0aWVzIGJlZm9yZSB0aGUgbmV3IGRhdGEgcG9pbnQKYGBgCgpgYGB7cn0KcHJlZGljdGlvbiRwb3N0ZXJpb3IgIyB0aGUgcG9zdGVyaW9yIHByb2JzIGZvciB0aGUgY2xhc3NlcyBnaXZlbiB0aGlzIG5ldyBkYXRhIHBvaW50CmBgYAoKYGBge3J9CnBsb3QoWCxjb2w9WSwgcGNoPTE5KQojIExldCdzIHBsb3QgaXQgd2l0aCB0aGUgY29sb3IgZm9yIHRoZSBjbGFzcyBpdCBwcmVkaWN0czoKcG9pbnRzKG5ldy5kYXRhJFgxLCBuZXcuZGF0YSRYMiwgcGNoPTE3LCBjb2w9cHJlZGljdGlvbiRjbGFzcykKIyBQbG90IHRoZSBtZWFuczoKcG9pbnRzKG1vZGVsJG1lYW5zLCBwY2g9IisiLCBjb2w9MTozKQpgYGAKCmBgYHtyfQpsaWJyYXJ5KGtsYVIpICMgQ2xhc3NpZmljYXRpb24gYW5kIHZpc3VhbGl6YXRpb24gcGFja2FnZQoKcGFydGltYXQoeD1teS5kYXRhWywtM10sIGdyb3VwaW5nPWFzLmZhY3RvcihteS5kYXRhWywzXSksIG1ldGhvZD0ibGRhIiwgCiAgICAgICAgIGNvbC5tZWFuPTEsIGltYWdlLmNvbG9ycyA9IGMoImxpZ2h0Z3JleSIsInJlZCIsImdyZWVuIikpCmBgYAoKYGBge3J9Cm1vZGVsIDwtIHFkYShZIH4gLiAsIGRhdGE9bXkuZGF0YSkKCm5ldy5kYXRhIDwtIGRhdGEuZnJhbWUoWDE9YygyKSwgWDI9Yyg1LjEpKSAjIG5ldyBkYXRhIHBvaW50IHNob3VsZCBiZSBjbGFzcyAzCnByZWRpY3Rpb24gPC0gcHJlZGljdChtb2RlbCwgbmV3LmRhdGEpCnByZWRpY3Rpb24kcG9zdGVyaW9yCmBgYAoKYGBge3J9CnBhcnRpbWF0KHg9bXkuZGF0YVssLTNdLCBncm91cGluZz1hcy5mYWN0b3IobXkuZGF0YVssM10pLCBtZXRob2Q9InFkYSIsIAogICAgICAgICBjb2wubWVhbj0xLCBpbWFnZS5jb2xvcnMgPSBjKCJsaWdodGdyZXkiLCJyZWQiLCJncmVlbiIpKQpgYGAKCiMjIExEQSBmb3IgRGltZW5zaW9uYWxpdHkgUmVkdWN0aW9uIGJlZm9yZSBDbGFzc2lmaWNhdGlvbgoKYGBge3J9CnNldC5zZWVkKDEwMSkgCiMgUmVhZCBTYW1wbGUgRGF0YQpteS5kYXRhIDwtIHJlYWQuY3N2KHVybCgiaHR0cDovL2FyY2hpdmUuaWNzLnVjaS5lZHUvbWwvbWFjaGluZS1sZWFybmluZy1kYXRhYmFzZXMvd2luZS93aW5lLmRhdGEiKSwKICAgICAgICAgICAgICAgICAgICBoZWFkZXI9RkFMU0UpClkgPC0gbXkuZGF0YVssMV0gICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBjbGFzc2VzClggPC0gYXMuZGF0YS5mcmFtZShzY2FsZShteS5kYXRhWywyOjE0XSkpICAgIyBzY2FsaW5nIGRhdGEgKGllLCBtZWFuPTAgJiBzZD0xIGZvciBhbGwgWGkpCm5hbWVzKFgpIDwtIHBhc3RlMCgiWCIsMToxMykKaGVhZChjYmluZChzaWduaWYoWCwzKSxZKSwgbj0xMCkKYGBgCgpgYGB7cn0KdHJhaW4gPC0gc2FtcGxlKDE6ZGltKFgpWzFdLDUwKSAgIyBkZWZpbmUgdGhlIHRyYWluaW5nIHNldCBpbmRleGVzCgojIEZpcnN0IHRlc3Qgd2l0aCBhIGtubiAKbGlicmFyeShjbGFzcykKcHJlZGljdGlvbiA8LSBrbm4oWFt0cmFpbixdLCBYWy10cmFpbixdLCBZW3RyYWluXSwgaz0xKQp0YWJsZTEgPC0gdGFibGUob2xkPVlbLXRyYWluXSwgbmV3PXByZWRpY3Rpb24pCnRhYmxlMQpgYGAKCmBgYHtyfQpsaWJyYXJ5KE1BU1MpCm1vZGVsIDwtIGxkYShZIH4gLiAsIGRhdGE9Y2JpbmQoWCxZKSwgc3Vic2V0PXRyYWluKQptb2RlbCRwcmlvciAgICMgdGhlIHByaW9yIHByb2JhYmlsaXRpZXMgdXNlZCAoYnkgZGVmYXVsdCwgdGhlIGNsYXNzIHByb3BvcnRpb25zIGZvciB0aGUgdHJhaW5pbmcgc2V0KQpgYGAKCmBgYHtyfQptb2RlbCRzY2FsaW5nICMgdGhlIHJlc3VsdGluZyBwcm9qZWN0aW9uIG1hdHJpeCB0byBwcm9qZWN0IG91ciBkYXRhc2V0IGludG8gYSAyLWRpbWVuc2lvbmFsIHZlY3RvciBzcGFjZQpgYGAKCmBgYHtyfQpwcm9qZWN0ZWQuZGF0YSA8LSBhcy5tYXRyaXgoWCkgJSolIG1vZGVsJHNjYWxpbmcgICMgbWFrZSB0aGUgcHJvamVjdGlvbiB0byAyRCBzcGFjZQpoZWFkKHByb2plY3RlZC5kYXRhKQpgYGAKCmBgYHtyfQpwbG90KHByb2plY3RlZC5kYXRhLCBjb2w9WSkKYGBgCgpgYGB7cn0KcHJlZGljdGlvbiA8LSBrbm4ocHJvamVjdGVkLmRhdGFbdHJhaW4sXSwgcHJvamVjdGVkLmRhdGFbLXRyYWluLF0sIFlbdHJhaW5dLCBrPTEpICMgTm93IGxldCdzIHJlYXBwbHkga25uOgp0YWJsZTIgPC0gdGFibGUob2xkPVlbLXRyYWluXSwgbmV3PXByZWRpY3Rpb24pCnRhYmxlMgpgYGAKCiMjIExEQSBwcm9qZWN0aW9ucwoKYGBge3J9CmxpYnJhcnkoTUFTUykKClggPC0gc2NhbGUoYXMubWF0cml4KGlyaXNbLC01XSkpICMgYmV0dGVyIHNjYWxlIG1lYW4gYW5kIHZhcmlhbmNlIGJlZm9yZSBMREEKWSA8LSB1bmNsYXNzKGlyaXMkU3BlY2llcykKaXJpczEgPC0gZGF0YS5mcmFtZShYPVgsIFk9WSkKY29sbmFtZXMoaXJpczEpIDwtIGNvbG5hbWVzKGlyaXMpCmhlYWQoaXJpczEpCmBgYAoKYGBge3J9Cm1vZGVsIDwtIGxkYShTcGVjaWVzIH4gLiAsIGRhdGE9aXJpczEsIHByaW9yPWMoMSwxLDEpLzMpCgpwbG90KGlyaXMxWywiU2VwYWwuTGVuZ3RoIl0sIGlyaXMxWywiUGV0YWwuTGVuZ3RoIl0sIAogICAgIGNvbD1jKCJibHVlIiwiZ3JlZW4iLCJyZWQiKVtpcmlzMSRTcGVjaWVzXSwgcGNoPTE5LAogICAgIHhsYWI9IlNlcGFsIExlbmd0aCIsIHlsYWI9IlBldGFsLkxlbmd0aCIpCgptZWFucyA8LSBtb2RlbCRtZWFucwpwb2ludHMobWVhbnNbLGMoMSwzKV0sIHBjaD0zLCBsd2Q9MiwgY29sPSJwdXJwbGUiKQpgYGAKCmBgYHtyfQptb2RlbApgYGAKCmBgYHtyfQptb2RlbCRzY2FsaW5nICMgdGhlIHBhcmFtZXRlcnMgb2YgdGhlIGxpbmVhciBkaXNjcmltaW5hbnQgZnVuY3Rpb25zCmBgYAoKYGBge3J9CnByZWQgPC0gcHJlZGljdChtb2RlbCwgaXJpc1ssLTVdKQoKIyBUaGUgbmV4dCBob3Jpem9udGFsIGF4aXMgYXJlIG1lYW5pbmdsZXNzLCB0aGV5IGRlcGVuZHMgb24gdGhlIHNhbXBsZSBvcmRlciBvZiB0aGUgZGF0YXNldApoZWFkKHByZWQkeFssMV0pICMgY29udGFpbnMgdGhlIHZhbHVlcyBvZiB0aGUgZGF0YXNldCBvYnNlcnZhdGlvbnMgZm9yIHRoZSBmaXJzdCBkaXNjcmltaW5hbnQgZnVuY3Rpb24KYGBgCgpgYGB7cn0KcGxvdChwcmVkJHhbLDFdLCBjb2w9YygiYmx1ZSIsImdyZWVuIiwicmVkIilbaXJpczEkU3BlY2llc10sIHBjaD0xOSkgIyB3ZSBjYW4gcGxvdCB0aGVtCmBgYAoKYGBge3J9CiMgTm90aWNlIHRoYXQgdGhlIDJuZCBkaXNjcmltaW5hbnQgZnVuY3Rpb24gZG9lcyBub3Qgc2VwYXJhdGUgdGhhdCB3ZWxsIHRoZSAybmQgJiAzcmQgY2xhc3MKcGxvdChwcmVkJHhbLDJdLCBjb2w9YygiYmx1ZSIsImdyZWVuIiwicmVkIilbaXJpczEkU3BlY2llc10sIHBjaD0xOSkgIyB3ZSBjYW4gcGxvdCB0aGVtCmBgYAoKYGBge3J9CnZlYyA8LSBjKG1vZGVsJHNjYWxpbmdbMSwxXSwgbW9kZWwkc2NhbGluZ1szLDFdKQp2ICAgPC0gdmVjIC8gc3FydChzdW0odmVjXjIpKSAgIyBtYWtlIGl0IGEgdW5pdCB2ZWN0b3IKbGRhMS5wb2ludHMgPC0gYXMubWF0cml4KGlyaXMxWyxjKDEsMyldKSAlKiUgdiAlKiUgdCh2KSAjIHRvIHByb2plY3QgcG9pbnQgWCBpbnRvIHVuaXQgdmVjdG9yIHYganVzdCBjYWxjdWxhdGUgWC52LnZeVAoKcGxvdChpcmlzMVssIlNlcGFsLkxlbmd0aCJdLCBpcmlzMVssIlBldGFsLkxlbmd0aCJdLCAKICAgICBjb2w9YygiYmx1ZSIsImdyZWVuIiwicmVkIilbaXJpczEkU3BlY2llc10sIHBjaD0xOSwKICAgICB4bGFiPSJTZXBhbCBMZW5ndGgiLCB5bGFiPSJQZXRhbC5MZW5ndGgiLCAsICBtYWluPSIxc3QgZGlzY3JpbWluYW50IGZ1bmN0aW9ucyIpCnNlZ21lbnRzKC12ZWNbMV0sLXZlY1syXSx2ZWNbMV0sdmVjWzJdKQojIHBvaW50cyhsZGExLnBvaW50cyAsIGNvbD1jKCJibHVlIiwiZ3JlZW4iLCJyZWQiKVtpcmlzMSRTcGVjaWVzXSwgcGNoPTE4KSAjIGRyYXcgcHJvamVjdGlvbiBwb2ludApmb3IoaSBpbiAxOm5yb3coaXJpczEpKSB7CiAgc2VnbWVudHMoaXJpczFbaSwxXSwgaXJpczFbaSwzXSwgbGRhMS5wb2ludHNbaSwxXSwgbGRhMS5wb2ludHNbaSwyXSwgCiAgICAgICAgICAgbHR5PTIsIGNvbD1jKCJibHVlIiwiZ3JlZW4iLCJyZWQiKVtpcmlzMVtpLF0kU3BlY2llc10pCn0KYGBgCgpgYGB7cn0KdmVjIDwtIGMobW9kZWwkc2NhbGluZ1sxLDJdLCBtb2RlbCRzY2FsaW5nWzMsMl0pCnYgICA8LSB2ZWMgLyBzcXJ0KHN1bSh2ZWNeMikpCmxkYTIucG9pbnRzIDwtIGFzLm1hdHJpeChpcmlzMVssYygxLDMpXSkgJSolIHYgJSolIHQodikKCnBsb3QoaXJpczFbLCJTZXBhbC5MZW5ndGgiXSwgaXJpczFbLCJQZXRhbC5MZW5ndGgiXSwgCiAgICAgY29sPWMoImJsdWUiLCJncmVlbiIsInJlZCIpW2lyaXMxJFNwZWNpZXNdLCBwY2g9MTksCiAgICAgeGxhYj0iU2VwYWwgTGVuZ3RoIiwgeWxhYj0iUGV0YWwuTGVuZ3RoIiwgLCAgbWFpbj0iMm5kIGRpc2NyaW1pbmFudCBmdW5jdGlvbnMiKQpzZWdtZW50cygtMip2ZWNbMV0sLTIqdmVjWzJdLDIqdmVjWzFdLDIqdmVjWzJdKQojIHBvaW50cyhsZGEyLnBvaW50cyAsIGNvbD1jKCJibHVlIiwiZ3JlZW4iLCJyZWQiKVtpcmlzMSRTcGVjaWVzXSwgcGNoPTE4KSAjIGRyYXcgcHJvamVjdGlvbiBwb2ludApmb3IoaSBpbiAxOm5yb3coaXJpczEpKSB7CiAgc2VnbWVudHMoaXJpczFbaSwxXSwgaXJpczFbaSwzXSwgbGRhMi5wb2ludHNbaSwxXSwgbGRhMi5wb2ludHNbaSwyXSwgCiAgICAgICAgICAgbHR5PTIsIGNvbD1jKCJibHVlIiwiZ3JlZW4iLCJyZWQiKVtpcmlzMVtpLF0kU3BlY2llc10pCn0KYGBgCgojIyMgQ2FuZ3Jlam9zCgpgYGB7cn0KbGNyYWJzIDwtIGxvZyhjcmFic1ssNDo4XSkKZGNyYWJzLmxkYSA8LSBsZGEoY3JhYnMkc2V4fiBGTCArIFJXICsgQ0wgKyBDVywgbGNyYWJzKQpkY3JhYnMubGRhJHNjYWxpbmcKYGBgCgpgYGB7cn0KdGFibGUoY3JhYnMkc2V4LCBwcmVkaWN0KGRjcmFicy5sZGEpJGNsYXNzKQpgYGAKCmBgYHtyfQpjcmFicy5ncnAgPC0gYygiQiIsICJiIiwgIk8iLCAibyIpW3JlcCgxOjQsIGVhY2ggPSA1MCldCmRjcmFicy5sZGE0IDwtIGxkYShjcmFicy5ncnB+IEZMICsgUlcgKyBDTCArIENXLCBsY3JhYnMpCmRjcmFicy5sZGE0CmBgYAoKYGBge3J9CmRjcmFicy5wcjQgPC0gcHJlZGljdChkY3JhYnMubGRhNCwgZGltZW4gPSAyKQpkY3JhYnMucHIyIDwtIGRjcmFicy5wcjQkcG9zdFssIGMoIkIiLCAiTyIpXSAlKiUgYygxLCAxKQp0YWJsZShjcmFicyRzZXgsIGRjcmFicy5wcjIgPiAwLjUpCmBgYAoKYGBge3J9CmNyLnQgPC0gZGNyYWJzLnByNCR4WywgMToyXQplcXNjcGxvdChjci50LCB0eXBlID0gIm4iLCB4bGFiID0gIkZpcnN0IExEIiwgeWxhYiA9ICJTZWNvbmQgTEQiKQp0ZXh0KGNyLnQsIGxhYmVscyA9IGFzLmNoYXJhY3RlcihjcmFicy5ncnApKQpwZXJwIDwtIGZ1bmN0aW9uKHgsIHkpIHsKICAgIG0gPC0gKHgreSkvMgogICAgcyA8LSAtICh4WzFdIC0geVsxXSkvKHhbMl0gLSB5WzJdKQogICAgYWJsaW5lKGMobVsyXSAtIHMqbVsxXSwgcykpCiAgICBpbnZpc2libGUoKQp9CmNyLm0gPC0gbGRhKGNyLnQsIGNyYWJzJHNleCkkbWVhbnMgCnBvaW50cyhjci5tLCBwY2ggPSAzLCBta2ggPSAwLjMpCnBlcnAoY3IubVsxLCBdLCBjci5tWzIsIF0pCmNyLmxkYSA8LSBsZGEoY3IudCwgY3JhYnMuZ3JwKQp4IDwtIHNlcSgtNiwgNiwgMC4yNSkKeSA8LSBzZXEoLTIsIDIsIDAuMjUpClhjb24gPC0gbWF0cml4KGMocmVwKHgsbGVuZ3RoKHkpKSwgcmVwKHksIGVhY2ggPSBsZW5ndGgoeCkpKSwsMikKY3IucHIgPC0gcHJlZGljdChjci5sZGEsIFhjb24pJHBvc3RbLCBjKCJCIiwgIk8iKV0gJSolIGMoMSwxKQpjb250b3VyKHgsIHksIG1hdHJpeChjci5wciwgbGVuZ3RoKHgpLCBsZW5ndGgoeSkpLApsZXZlbHMgPSAwLjUsIGxhYmV4ID0gMCwgYWRkID0gVCwgbHR5PSAzKQpgYGA=