Introducción
Las notas contenidas en este documento, al igual que algunas imágenes han sido tomadas en su mayoría de la serie de videos del canal de YouTube Brandon Foltz.
El objetivo es plasmar algunos fundamentos teóricos de la Regresión Lineal Simple haciendo uso del lenguaje R.
Los supuestos matemáticos de la regresión lineal simple no se presentan en el documento (linealidad, independencia, homocedasticidad, etc.).
Caso Práctico
Propinas por el servicio: supongamos que somos dueños de un pequeño restaurante y deseamos predecir el monto de las propinas que se generan.
El monto de la propina está relacionado con el valor total de la factura, es decir, una propina dejada por valor de una factura de 5 dólares será menor, que la esperada por una factura de 50 dólares.
Se decide tomar entonces durante una noche, los datos de las propinas recibidas de seis comidas aleatorias. Inicialmente sólo se toma la información del monto de las propinas en dólares, pero no el valor de la factura pagada.
Los datos se muestran en la siguiente tabla:
propinas <- data.frame(plato = c(1, 2, 3, 4, 5, 6),
propina = c(5, 17, 11, 8, 14, 5))
propinas
Pregunta: ¿cómo se podría predecir el valor de propinas futuras, sólo con esta información?.
Para dar respuesta a esa pregunta, la mejor herramienta que se tiene es calcular la media, ya que sólo disponemos de una sola variable (la propina).
El promedio de las propinas es:
\[\bar{y} = \frac{5+17+11+8+14+5}{6}=10\]
[1] 10
Con una sola variable y sin ninguna información adicional, el mejor predictor para la siguiente medición es la media de la muestra.
Con una sola variable la mejor predicción posible de obtener es de 10 US$, para el valor de las propinas.
Por lo tanto la variabilidad está explicada por el valor de las propinas en si mismas.
Gráfico propinas
El gráfico muestra los valores de las propinas dadas por cada comida y la línea roja punteada señala la media de las propinas.
library(tidyverse)
propinas %>%
ggplot(mapping = aes(x = plato, y = propina))+
geom_point(color = "cadetblue")+
labs(x = "Plato",
y = "Propina US$",
title = "Valor Propina",
caption = "La línea a trazos represeta el promedio.")+
scale_x_continuous(n.breaks = 6)+
scale_y_continuous(n.breaks = 5)+
geom_hline(yintercept=10, linetype="dashed", color = "chocolate")+
geom_text(aes(label=propina),hjust= -0.5, vjust=0.5)+
theme_bw()

Residuales
¿Qué tan buena es esta predicción hecha por la línea que representa la media?.
Algunos de los valores están por encima y otros por debajo de esta línea.
Tomando la distancia medida desde la media (10 US$) a los valores observados, obtenemos los valores residuales y así verificar la bondad de ajuste del “modelo”.

\(Residuales = x_i - \bar{x}\)
luego,
\(Residual\ x_1 = 5-10 = -5\)
\(Residual\ x_2 = 17-10 = 7\)
\(Residual\ x_3 = 11-10 = 1\)
\(Residual\ x_4 = 8-10 = -2\)
\(Residual\ x_5 = 14-10 = 4\)
\(Residual\ x_6 = 5-10 = -5\)
Calculando los residuales con R:
residuales <- (propinas$propina - mean(propinas$propina))
residuales
[1] -5 7 1 -2 4 -5
Estos residuales son llamados también, el error, el cual indica cuánto se aleja el valor observado, de la linea de ajuste (media).
Si se suman los residuales que se encuentran por debajo de la media se obtiene un valor de -12.
Y si se suman los residuales que se encuentran por encima de la línea de ajuste el valor obtenido es 12.
Al sumar los residuales:
\[\sum(Residuales) = -12+12=0\]
[1] 0
La suma algebraica de los residuales siempre es cero.
Suma Cuadrada del Error
Se elevará el valor de los residuales al cuadrado, como lo muestra la siguiente tabla, con el objetivo de obtener valores positivos de los mismos, de la misma manera que en la desviación estándar.
\[Des.Est = \sqrt{\frac{\sum(x-\bar{x})^2}{n}}\]
data.frame(plato = propinas$plato,
residuales = residuales,
"resid^2" = (residuales^2))
La suma de estos residuales que estan elevados al cuadrado, se conoce como la Suma de Cuadrados del Error o la Suma de Cuadrados de los Residuales (SCE). En inglés inglés Sum of Squares Error (SSE).
Obteniendo la suma de cuadrados del error:
\[SCE = 25+49+1+4+16+25=120\]
[1] 120

Objetivo
El objetivo de la regresión lineal es crear un modelo lineal que minimice la suma de cuadrados del error.
Su propósito principal es estimar la función de regresión poblacional con base en la función de regresión muestral.
Dicho de otra manera, el objetivo es obtener un modelo (línea de ajuste) agregando una variable independiente que minimice el tamaño de los cuadrados tanto como sea posible.
Se obtiene una línea que se ajusta mejor a los datos tomados del problema y que miminiza los residuales.
Estadística Bivariada
El estudio del análisis de varianza ANOVA y de la correlación, se relacionan con la regresión lineal simple.

“Repaso de Álgebra”: Línea Recta

Modelo de Regresión Lineal
Para el modelo de regresión lineal se hace necesario la existencia de dos variables, a diferencia del caso anterior en el cual sólo se contaba con información de una sola variable (propina).
\[y = mx+b\] El modelo de regresión lineal para la población de datos que se considere está dada por:
\[y = \beta_{0}+\beta_{1}x\]
\[\hat{y} = \hat{\beta_0}+\hat\beta_{1}X_i+\epsilon\]
En esencia es igual a la recta pendiente intercepto de arriba.
\(\beta_{0} =\) intercepto con el eje \(y\) del parámetro de la población.
\(\beta_{1}=\) pendiente del parámetro de la población.
\(\epsilon=\) término para el error, variación que no se puede explicar de la variable \(y\).
La función o ecuación que describe la regresión lineal simple es:
\[E(y)= \beta_{0}+\beta_{1}x\] El valor esperado de \(y\) es la media, para un valor dado de \(x\).
El valor esperado de \(y\) es realmente la media de una distribución alrededor de los valores de \(y\).
A manera de ejemplo y tomando la base de datos cars
de R, se presenta el modelo de regresión lineal para el conjunto de datos:
library(ggpmisc)
cars %>%
ggplot(mapping = aes(x = dist, y = speed))+
geom_point()+
geom_smooth(method = "lm", se = F)+
labs(title = "Línea de Regresión", y = "Distancia", x = "Velocidad", subtitle = "Base de Datos cars")+
stat_poly_eq(aes(label = paste(..eq.label.. ,
..rr.label.. ,
sep = "~~~~")),
formula = y ~ x, parse = TRUE,
label.x.npc = 0.5,
color = "blue")

Ecuación de regresión con estimaciones
Si se conocen los valores para los parámetros \(\beta_{0}\) y \(\beta_{1}\), es posible usar la ecuación de regresión lineal simple: \[E(y)=\beta_{0}+\beta_{1}x\]
Pero en realidad casi nunca se conocen los valores de los parámetros de la población, por lo tanto se deben estimar usando una muestra de los datos.
Cuando usamos una muestra de los datos la ecuación se escribe como:
\[\hat{y}= b_{0}+b_{1}x\]
donde \(\hat{y}\) es el estimador para el valor esperado de \(y\).
\(E(y)\) es la media del valor \(y\) para un valor dado de \(x\).
Percepción Geométrica


NOTA: en la literatura, en varios casos la diferencia entre las ecuaciones, se refleja en el uso de las letras b minúsculas, para remarcar que se trabaja con una muestra de la población.
Aplicado al ejemplo en el cual sólo se tiene información de una variable, la propina, el valor de la pendiente sería cero, ya que de hecho la variable \(x\) no existe, por lo tanto:

El valor de \(\hat{y}\) es \(10\) para todo valor de \(x\).
Siguiendo con el mismo ejemplo del restaurante si se agrega la información del total de la factura y el valor de la propina; en este caso se dice que el valor de la propina depende del monto total de la factura (US$).
Agregando una nueva columna a los datos:
propinas$factura <- c(34, 108, 64, 88, 99, 51)
propinas
Hipotéticamente se dice que el valor de la propina dependerá del valor total de la factura, una factura por un valor bajo dará como resultado una propina más baja comparada con el monto de una propina dejada por una factura de más alto valor.
Si la línea de tendencia resultante observada, reduce la suma de cuadrados del error, obtenida en el caso en el que se tenía sólo la variable propinas que usaba el modelo de la media de \(10 US\$\), se dice que el modelo de regresión es mejor cualitativamente comparado con el modelo de la media.
Se podrá calcular de manera cuantitativa cuánto es mejor un modelo en comparación con otro.
Método de Mínimos Cuadrados
Ahora que se tiene la información en pares, sobre el valor total de la factura y la propina dejada de esa factura; se desea conocer en qué grado el monto de la propina puede ser predicha por el valor total de la factura.
Se puede afirmar entonces que la variable propina es la variable dependiente y la variable independiente es la factura.
Este planteamiento es importante hacerlo para determinar el rol de las variables en nuestro modelo, no tiene sentido alguno afirmar que el valor total de la factura está dado por el monto de la propina.
Criterio de Mínimos Cuadrados
\[min\sum(y_{i}-\hat{y}_i)^2\]
\(y_{i}=\) es el valor observado de la variable dependiente (propina).
\(\hat{y}_i=\) valor estimado (predicho) de la variable dependiente (valor predicho de la propina). Basado en un modelo de regresión.
Se tendrán dos valores para cada valor de \(x\), el valor observado (real) y el valor predicho.
El objetivo es minimizar la suma de las diferencias cuadradas entre el valor observado para la variable dependiente (\(y_i\)) y el valor predicho o estimado de la variable dependiente (\(\hat{y}_i\)) que esta dado por la línea de regresión. Suma de los cuadrados del error.
Entonces la suma de los cuadrados residuales debe ser mucho menor que la obtenida sólo con la variable dependiente; \(\beta_1=0\), \(\hat{y}=10\) para cada valor de \(x\). La suma de los cuadrados debe ser mucho menor que \(120\) que fué la obtenida.
Gráfico de dispersión
Un gráfico de dispersión arroja información acerca de el comportamiento que siguen los datos, hacer uso de una escala acorde a los datos, facilita la interpretación del gráfico.
Infinitas rectas pueden ajustarse en menor o mayor medida a los datos, la idea es obtener aquella que minimice entonces, la suma de los cuadrados del error.
En comparación con los residuales obtenidos con la media de las propinas (línea horizonal), la suma de los cuadrados residuales de las rectas con pendiente diferente a cero, es menor.
propinas %>%
ggplot(mapping = aes(x = factura, y = propina))+
geom_point(color = "blue")+
geom_smooth(method = "lm", se = FALSE, color = "orange", linetype = "dashed")+
labs(x = "Factura",
y = "Propina",
title = "Posibles Líneas de Regresión")+
geom_abline(slope = 0.146,
intercept = 1,
linetype = "dashed",
color = "forestgreen",
size = 1)+
geom_abline(slope = 0.146,
intercept = -2,
linetype = "dashed",
color = "purple",
size = 1)+
geom_text(aes(label=propina),hjust=-0.5, vjust=0)+
theme_bw()

Correlación
El coeficiente de correlación de Pearson es una medida lineal entre dos variables aleatorias cuantitativas. A diferencia de la covarianza, la correlación es independiente de la escala de medida.
Este coeficiente puede ser de dos tipos:
Útil para obtener el coeficiente de correlación de el modelo de regresión lineal.
\[r(x,y) = \frac{Cov_{x,y}}{\sigma_x \sigma_y} = \frac{\sum(x_i - \bar{x})(y_i - \bar{y})}{\sqrt{\sum(x_i - \bar{x})^2}*\sqrt{\sum(y_i - \bar{y})^2}}\]

El coeficiente de correlación \(r\) permite verificar si la relación obtenida en los datos es lineal, positiva o negativa; también indica que no hay correlación lineal, en caso de ser cero. Entre mas cercano es a 1 o -1 es mas fuerte, entre mas cercano a 0 es débil hasta llegar hacerse nula.
- Correlación perfecta negativa = \(-1\) .
- No existe correlación = \(0\).
- Correlación perfecta positiva = \(1\).
Calculando la correlación de las variables con R:
cor(propinas$propina, propinas$factura)
[1] 0.865665
También se puede obtener la matriz de correlación de las variables del conjunto de datos:
plato propina factura
plato 1.0000000 -0.1309307 0.1511229
propina -0.1309307 1.0000000 0.8656650
factura 0.1511229 0.8656650 1.0000000
Estadística Descriptiva de las Variables
Conocer las variables de manera gŕafica y numérica, es una buena práctica que arroja información importante para validar las condiciones que permitan la aplicación del modelo.
Obtener las medias para cada una de las variables:
[1] 74
[1] 10
El punto que se ubica en la coordenada \(\bar{x} = 74\) , \(\bar{y} = 10\), se conoce como centroide, y es el punto formado por las medias de las variables.
La línea de regresión que mejor se ajuste pasará por el centroide.
Resumen tabular de los valores numéricos necesarios para el modelo de regresión lineal:
propinas %>%
mutate(desvprop = propina-mean(propina),
desvfact = factura-mean(factura),
proddesv = desvfact*desvprop,
cuadesvp = desvprop^2,
cuadesvf = desvfact^2)
Otros valores numéricos bastante útiles son:
[1] 76
[1] 29.00345
[1] 841.2
cov(propinas$propina, propinas$factura)
[1] 123
Obtener la Línea de Regresión
\[\hat{y}_i=b_{0}+b_{1}x_{i}\]
Pendiente: \[b_{1}=\frac{\sum(x_{i}-\bar{x})(y_{i}-\bar{y})}{\sum(x_{i}-\bar{x})^2} = 0.1462\]
donde,
\(\bar{x}=\) media de la variable independiente.
\(\bar{y}=\) media de la variable dependiente.
\(x=\) valor de la variable independiente.
\(y=\) valor de la variable dependiente.
Intercepto:
\[b_{0}= \bar{y}-b_{1}\bar{x} = -0.8188\]
Entonces, la fórmula que describe la línea de regresión lineal en términos de las variables es:
\[propina = -0.8188+(0.1462)factura\]
Para obtener la representación geométrica de la línea de regresión con R, hacemos uso de el método lm
.
propinas %>%
ggplot(mapping = aes(x = factura, y = propina))+
geom_point(color = "cadetblue")+
geom_text(aes(label=propina),hjust=0, vjust=0)+
geom_smooth(method = "lm", se = FALSE, color = "firebrick2")+
geom_point(mapping = aes(x = 74, y = 10), color = "blue", size = 2)+
annotate("text",
label = "Centroide",
x = 83,
y = 10,
color = "blue")+
labs(x = "Factura",
y = "Propina",
title = "Línea de Regresión")+
stat_poly_eq(aes(label = paste(..eq.label.. ,
sep = "~~~~")),
formula = y ~ x, parse = TRUE,
label.x.npc = 0.5,
color = "firebrick")+
theme_bw()

Interpretación
\[\hat{y_i} = 0.1462x-0.8188\]
El valor de la pendiente para este caso, significa, que por cada dólar que aumenta el valor de la factura, esperaríamos que el monto para la propina aumente en \(0.1462\) \(US\$\), aproximadamente \(15\) centavos de dólar.
La interpretación del intercepto, puede o nó, tener un significado en el “mundo real”, dependerá entonces de el fenómeno que se esté estudiando. En este caso en particular, si el valor de la factura es cero, el modelo predice un valor de propina de \(-0.8188\) \(US\$\), lo cual no tiene sentido en la vida real ya se obtiene un valor negativo de propina.
Es importante anotar, que el modelo de regresión es único para el conjunto de datos que representa; adicionar o cambiar datos generará un cambio en el modelo.
Ajuste y Coeficiente de Determinación
¿Qué tan bien se ajusta la línea de regresión a los datos, comparado cuando se usa sólo el promedio de la variable dependiente?.
Recordemos que cuando calculamos la suma de los cuadrados del error para la variable dependiente se obtuvo: \(SSE=120\). La suma total de los cuadrados del error (SST = SSE) nunca será mayor a 120 para este conjunto de datos.
Tengamos presente también, que el objetivo es reducir el SSE a través de la línea que mejor se ajuste al cojunto de datos, ya que cuanto mejor se ajuste el modelo, menor será el valor del SSE.

Vamos a obtener la suma de cuadrados del error debido a la regresión (SSR), la cual es la diferencia entre SST y SSE.
Obtengamos con el modelo, los valores que predice para el monto de las propinas:
\[\hat{y_i} = 0.1462x_i-0.2188\]
donde,
\(x_i = factura_i\),
\(\hat{y} = predicción\ propina\)
Por ejemplo para el plato 3 se tiene una factura por valor de \(64US\$\). Por lo tanto el valor que predice el modelo es:
\[\hat{y_i} = 0.1462(64)-0.2188 = 8.5365\]
Ahora veamos en la tabla los valores predichos por el modelo:
mod1 <- lm(propina ~ factura, data = propinas)
propinas %>%
mutate(prop_pred = predict(mod1))
Error Predicciones
La diferencia que existe entre el valor observado y el valor predicho, es el error, es decir la distancia que hay entre el la predicción y el valor observado. También se les conoce como residuales.
propinas %>%
mutate(prop_pred = predict(mod1)) %>%
ggplot(mapping = aes(y = prop_pred, x = factura))+
geom_smooth(method = "lm", se = F)+
geom_point(color = "darkred")+
geom_text(aes(label=round(prop_pred, digits = 2)),hjust=0, vjust=1.5, color = "darkred")+
geom_point(mapping = aes(x = factura, y = propina), color = "black")+
labs(title = "Error Modelo de Regresión Lineal",
x = "Factura",
y = "Propina")+
stat_poly_eq(aes(label = paste(..eq.label.. ,
sep = "~~~~")),
formula = y ~ x, parse = TRUE,
label.x.npc = 0.5,
color = "blue")+
theme_bw()

- Tabla de error y cuadrado del error
propinas %>%
mutate(prop_pred = predict(mod1),
error = propina-prop_pred,
cuad_error = (error)^2)

La suma de los cuadrados del error SSE es 30.074893:
sum((propinas$propina - predict(mod1))^2)
[1] 30.07489


Podemos apreciar el comparativo de los valores de SSE cuando sólo se toma como predictor el promedio de la variable y cuando aplicamos regresión lineal simple; esto resume el objetivo de la regresión lineal, el cual es reducir el valor de la suma de los cuadrados del error.
El modelo redujo el valor del error, de 120 a 30.075, es decir que se redujo en 89.925 unidades.
\[SST = SSR + SSE\]
\[120 = 89.925 + 30.075\] Como se había mencionado anteriormente, el valor total máximo que se puede obtener es de 120, pero para el caso en el que se aplica la regresión lineal se obtiene un valor para \(SSE=30.075\) y para la suma de los cuadrados del error debido a la regresión, es de \(SSR=89.925\).
Coeficiente de Determinación
¿Qué tan bien se ajusta la línea de regresión a los datos?.
En este punto es donde la regresión comienza a tener similitud con ANOVA; la suma total de los cuadrados SST es dividida entre SSR y SSE y luego se mide la relación entre SSR y SST.
Si SSR es grande, le corresponde una mayor parte de SST y, por lo tanto, SSE es más pequeño en relación con SST.
El coeficiente de determinación \(r^2\) está dado por:
\[r^2 = \frac{SSR}{SST} = \frac{89.925}{120} = 0.749375\]
Interpretación
El valor permite medir si el modelo es, o no , estadísticamente siginificativo.
De la misma manera el valor se puede dar como porcentaje, en este caso \(74.93\%\).
Es posible concluir, que el \(74.93\%\) de la suma total de los cuadrados, puede ser explicado aplicando la ecuación de regresión para predecir el monto de la propina. El resto (\(25.07\%\)) es debido al error.
De esta información podemos decir que el ajuste que se obtiene es bueno.
propinas %>%
ggplot(mapping = aes(x = factura, y = propina))+
geom_point(color = "orange")+
geom_text(aes(label=propina),hjust=0, vjust=0)+
geom_smooth(method = "lm", se = FALSE, color = "forestgreen")+
labs(x = "Factura",
y = "Propina",
title = "Coeficiente de Determinación")+
stat_poly_eq(aes(label = paste(..eq.label.. ,
..rr.label.. ,
sep = "~~~~")),
formula = y ~ x, parse = TRUE,
label.x.npc = 0.5,
color = "steelblue")+
theme_bw()

Diferencias entre la suma de cuadrados
\[SSE = \Sigma(y-\hat{y_i})^2\]
\[SST = \Sigma(y_i - \bar{y})^2\]
\[SSR = \Sigma(\hat{y_i}- \bar{y})^2\]

Resumen del Modelo
La función lm()
de R arroja un resumen del modelo de la regresión lineal:
mod1 <- lm(propina ~ factura, data = propinas)
resumen_modelo <- summary(mod1)
resumen_modelo
Call:
lm(formula = propina ~ factura, data = propinas)
Residuals:
1 2 3 4 5 6
0.8488 2.0285 2.4622 -4.0471 0.3445 -1.6369
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) -0.82026 3.32297 -0.247 0.8172
factura 0.14622 0.04228 3.458 0.0259 *
---
Signif. codes:
0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 2.742 on 4 degrees of freedom
Multiple R-squared: 0.7494, Adjusted R-squared: 0.6867
F-statistic: 11.96 on 1 and 4 DF, p-value: 0.02586
Es conveniente también acceder a los estadísticos del modelo de manera individual:
# predicciones del modelo
predict(mod1)
1 2 3 4 5
4.151213 14.971469 8.537803 12.047076 13.655492
6
6.636947
# coeficientes
coefficients(mod1)
(Intercept) factura
-0.8202568 0.1462197
# residuales
residuals(mod1)
1 2 3 4 5
0.8487874 2.0285307 2.4621969 -4.0470756 0.3445078
6
-1.6369472
# error estandar del ajuste
summary(mod1)$sigma
[1] 2.742029
# cuadrado del error
summary(mod1)$r.squared
[1] 0.7493759
Apliquemos ANOVA a nuestro modelo:
Analysis of Variance Table
Response: propina
Df Sum Sq Mean Sq F value Pr(>F)
factura 1 89.925 89.925 11.96 0.02586 *
Residuals 4 30.075 7.519
---
Signif. codes:
0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Hasta acá se tienen las herramientas básicas para aplicar el modelo de regresión lineal simple y algunas funciones de R para obtener algunos resultados descriptivos.
LS0tCnRpdGxlOiAiUmVncmVzacOzbiBMaW5lYWwgU2ltcGxlIGNvbiBSOiBGdW5kYW1lbnRvcyIKc3VidGl0bGU6ICJDYXNvIFByw6FjdGljbyIKYXV0aG9yOiAiQ1JHIgpkYXRlOiAiTWF5by0yMDIxIgpvdXRwdXQ6IAogICAgaHRtbF9ub3RlYm9vazoKICAgICAgdG9jOiB0cnVlCiAgICAgIHRvY19mbG9hdDogdHJ1ZQogICAgICB0aGVtZTogcmVhZGFibGUKICAgICAgZGZfcHJpbnQ6IHBhZ2VkCiAgICAgIGhpZ2hsaWdodDogZXNwcmVzc28KICAgICAgY3NzOiBkaXNlbm8uY3NzCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0UsIGZpZy53aWR0aCA9IDcsIGZpZy5oZWlnaHQgPSA1LCBmaWcuYWxpZ24gPSAiY2VudGVyIikKYGBgCgoKIyBJbnRyb2R1Y2Npw7NuCgpMYXMgbm90YXMgY29udGVuaWRhcyBlbiBlc3RlIGRvY3VtZW50bywgYWwgaWd1YWwgcXVlIGFsZ3VuYXMgaW3DoWdlbmVzIGhhbiBzaWRvIHRvbWFkYXMgZW4gc3UgbWF5b3LDrWEgIGRlIGxhIHNlcmllIGRlIHZpZGVvcyBkZWwgY2FuYWwgZGUgW1lvdVR1YmUgQnJhbmRvbiBGb2x0el0oaHR0cHM6Ly93d3cueW91dHViZS5jb20vd2F0Y2g/dj1aa2pQNVJKTFFGNCkuCgpFbCBvYmpldGl2byBlcyBwbGFzbWFyIGFsZ3Vub3MgZnVuZGFtZW50b3MgdGXDs3JpY29zIGRlIGxhICoqUmVncmVzacOzbiBMaW5lYWwgU2ltcGxlKiogaGFjaWVuZG8gdXNvIGRlbCBsZW5ndWFqZSBSLgoKKkxvcyBzdXB1ZXN0b3MgbWF0ZW3DoXRpY29zIGRlIGxhIHJlZ3Jlc2nDs24gbGluZWFsIHNpbXBsZSBubyBzZSBwcmVzZW50YW4gZW4gZWwgZG9jdW1lbnRvIChsaW5lYWxpZGFkLCBpbmRlcGVuZGVuY2lhLCBob21vY2VkYXN0aWNpZGFkLCBldGMuKS4qCgoKIyBDYXNvIFByw6FjdGljbwoKKlByb3BpbmFzIHBvciBlbCBzZXJ2aWNpbzoqIHN1cG9uZ2Ftb3MgcXVlIHNvbW9zIGR1ZcOxb3MgZGUgdW4gcGVxdWXDsW8gcmVzdGF1cmFudGUgeSBkZXNlYW1vcyBwcmVkZWNpciBlbCBtb250byBkZSBsYXMgcHJvcGluYXMgcXVlIHNlIGdlbmVyYW4uIAoKRWwgbW9udG8gZGUgbGEgcHJvcGluYSBlc3TDoSByZWxhY2lvbmFkbyBjb24gZWwgdmFsb3IgdG90YWwgZGUgbGEgZmFjdHVyYSwgZXMgZGVjaXIsIHVuYSBwcm9waW5hIGRlamFkYSBwb3IgdmFsb3IgZGUgdW5hIGZhY3R1cmEgZGUgNSBkw7NsYXJlcyBzZXLDoSBtZW5vciwgcXVlIGxhIGVzcGVyYWRhIHBvciB1bmEgZmFjdHVyYSBkZSA1MCBkw7NsYXJlcy4KClNlIGRlY2lkZSB0b21hciBlbnRvbmNlcyBkdXJhbnRlIHVuYSBub2NoZSwgbG9zIGRhdG9zIGRlIGxhcyBwcm9waW5hcyByZWNpYmlkYXMgZGUgc2VpcyBjb21pZGFzIGFsZWF0b3JpYXMuIEluaWNpYWxtZW50ZSBzw7NsbyBzZSB0b21hIGxhIGluZm9ybWFjacOzbiBkZWwgbW9udG8gZGUgbGFzIHByb3BpbmFzIGVuIGTDs2xhcmVzLCBwZXJvIG5vIGVsIHZhbG9yIGRlIGxhIGZhY3R1cmEgcGFnYWRhLiAKCkxvcyBkYXRvcyBzZSBtdWVzdHJhbiBlbiBsYSBzaWd1aWVudGUgdGFibGE6CgpgYGB7cn0KcHJvcGluYXMgPC0gZGF0YS5mcmFtZShwbGF0byA9IGMoMSwgMiwgMywgNCwgNSwgNiksCiAgICAgICAgICAgICAgICAgICAgICAgcHJvcGluYSA9IGMoNSwgMTcsIDExLCA4LCAxNCwgNSkpCgpwcm9waW5hcwpgYGAKCjxicj4KCioqUHJlZ3VudGE6Kiogwr9jw7NtbyBzZSBwb2Ryw61hIHByZWRlY2lyIGVsIHZhbG9yIGRlIHByb3BpbmFzIGZ1dHVyYXMsIHPDs2xvIGNvbiBlc3RhIGluZm9ybWFjacOzbj8uICAKClBhcmEgZGFyIHJlc3B1ZXN0YSBhIGVzYSBwcmVndW50YSwgbGEgbWVqb3IgaGVycmFtaWVudGEgcXVlIHNlIHRpZW5lIGVzIGNhbGN1bGFyIGxhIG1lZGlhLCB5YSBxdWUgc8OzbG8gZGlzcG9uZW1vcyBkZSB1bmEgc29sYSB2YXJpYWJsZSAobGEgcHJvcGluYSkuCgpFbCBwcm9tZWRpbyBkZSBsYXMgcHJvcGluYXMgZXM6CgokJFxiYXJ7eX0gPSBcZnJhY3s1KzE3KzExKzgrMTQrNX17Nn09MTAkJAoKYGBge3J9Cm1lYW4ocHJvcGluYXMkcHJvcGluYSkKYGBgCgoKQ29uIHVuYSBzb2xhIHZhcmlhYmxlIHkgc2luICBuaW5ndW5hIGluZm9ybWFjacOzbiBhZGljaW9uYWwsIGVsIG1lam9yIHByZWRpY3RvciBwYXJhIGxhIHNpZ3VpZW50ZSBtZWRpY2nDs24gZXMgbGEgbWVkaWEgZGUgbGEgbXVlc3RyYS4KCkNvbiB1bmEgc29sYSB2YXJpYWJsZSBsYSBtZWpvciBwcmVkaWNjacOzbiBwb3NpYmxlIGRlIG9idGVuZXIgZXMgZGUgMTAgVVMkLCBwYXJhIGVsIHZhbG9yIGRlIGxhcyBwcm9waW5hcy4gIAoKUG9yIGxvIHRhbnRvIGxhIHZhcmlhYmlsaWRhZCBlc3TDoSBleHBsaWNhZGEgcG9yIGVsIHZhbG9yIGRlIGxhcyBwcm9waW5hcyBlbiBzaSBtaXNtYXMuCgojIyBHcsOhZmljbyBwcm9waW5hcwoKRWwgZ3LDoWZpY28gbXVlc3RyYSBsb3MgdmFsb3JlcyBkZSBsYXMgcHJvcGluYXMgZGFkYXMgcG9yIGNhZGEgY29taWRhIHkgbGEgbMOtbmVhIHJvamEgcHVudGVhZGEgc2XDsWFsYSBsYSBtZWRpYSBkZSBsYXMgcHJvcGluYXMuCgpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCnByb3BpbmFzICU+JSAKICBnZ3Bsb3QobWFwcGluZyA9IGFlcyh4ID0gcGxhdG8sIHkgPSBwcm9waW5hKSkrCiAgZ2VvbV9wb2ludChjb2xvciA9ICJjYWRldGJsdWUiKSsKICBsYWJzKHggPSAiUGxhdG8iLAogICAgICAgeSA9ICJQcm9waW5hIFVTJCIsCiAgICAgICB0aXRsZSA9ICJWYWxvciBQcm9waW5hIiwKICAgICAgIGNhcHRpb24gPSAiTGEgbMOtbmVhIGEgdHJhem9zIHJlcHJlc2V0YSBlbCBwcm9tZWRpby4iKSsKICBzY2FsZV94X2NvbnRpbnVvdXMobi5icmVha3MgPSA2KSsKICBzY2FsZV95X2NvbnRpbnVvdXMobi5icmVha3MgPSA1KSsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9MTAsIGxpbmV0eXBlPSJkYXNoZWQiLCBjb2xvciA9ICJjaG9jb2xhdGUiKSsKICBnZW9tX3RleHQoYWVzKGxhYmVsPXByb3BpbmEpLGhqdXN0PSAtMC41LCB2anVzdD0wLjUpKwogIHRoZW1lX2J3KCkKYGBgCgojIyBSZXNpZHVhbGVzCgrCv1F1w6kgdGFuIGJ1ZW5hIGVzIGVzdGEgcHJlZGljY2nDs24gaGVjaGEgcG9yIGxhIGzDrW5lYSBxdWUgcmVwcmVzZW50YSBsYSBtZWRpYT8uCgpBbGd1bm9zIGRlIGxvcyB2YWxvcmVzIGVzdMOhbiBwb3IgZW5jaW1hIHkgb3Ryb3MgcG9yIGRlYmFqbyBkZSBlc3RhIGzDrW5lYS4KClRvbWFuZG8gbGEgZGlzdGFuY2lhIG1lZGlkYSBkZXNkZSBsYSBtZWRpYSAoMTAgVVMkKSBhIGxvcyB2YWxvcmVzIG9ic2VydmFkb3MsIG9idGVuZW1vcyBsb3MgdmFsb3JlcyByZXNpZHVhbGVzIHkgYXPDrSB2ZXJpZmljYXIgbGEgYm9uZGFkIGRlIGFqdXN0ZSBkZWwgIm1vZGVsbyIuCgohW10ocmVzaWR1YWxlcy5wbmcpCgo8YnI+CgokUmVzaWR1YWxlcyA9IHhfaSAtIFxiYXJ7eH0kCgpsdWVnbywKCiRSZXNpZHVhbFwgeF8xID0gNS0xMCA9IC01JAoKJFJlc2lkdWFsXCB4XzIgPSAxNy0xMCA9IDckCgokUmVzaWR1YWxcIHhfMyA9IDExLTEwID0gMSQKCiRSZXNpZHVhbFwgeF80ID0gOC0xMCA9IC0yJAoKJFJlc2lkdWFsXCB4XzUgPSAxNC0xMCA9IDQkCgokUmVzaWR1YWxcIHhfNiA9IDUtMTAgPSAtNSQgIAoKCkNhbGN1bGFuZG8gbG9zIHJlc2lkdWFsZXMgY29uIFI6CgpgYGB7cn0KcmVzaWR1YWxlcyA8LSAocHJvcGluYXMkcHJvcGluYSAtIG1lYW4ocHJvcGluYXMkcHJvcGluYSkpCnJlc2lkdWFsZXMKYGBgCgoKRXN0b3MgcmVzaWR1YWxlcyBzb24gbGxhbWFkb3MgdGFtYmnDqW4sIGVsIGVycm9yLCBlbCBjdWFsIGluZGljYSBjdcOhbnRvIHNlIGFsZWphIGVsIHZhbG9yIG9ic2VydmFkbywgZGUgbGEgbGluZWEgZGUgYWp1c3RlIChtZWRpYSkuCgpTaSBzZSBzdW1hbiBsb3MgcmVzaWR1YWxlcyBxdWUgc2UgZW5jdWVudHJhbiBwb3IgZGViYWpvIGRlIGxhIG1lZGlhIHNlIG9idGllbmUgdW4gdmFsb3IgZGUgYHIgc3VtKHJlc2lkdWFsZXNbYygxLCA0LCA2KV0pYC4KClkgc2kgc2Ugc3VtYW4gbG9zIHJlc2lkdWFsZXMgcXVlIHNlIGVuY3VlbnRyYW4gcG9yIGVuY2ltYSBkZSBsYSBsw61uZWEgZGUgYWp1c3RlIGVsIHZhbG9yIG9idGVuaWRvIGVzIGByIHN1bShyZXNpZHVhbGVzW2MoMiwgMywgNSldKWAuCgpBbCBzdW1hciBsb3MgcmVzaWR1YWxlczoKCiQkXHN1bShSZXNpZHVhbGVzKSA9IC0xMisxMj0wJCQKCmBgYHtyfQpzdW0ocmVzaWR1YWxlcykKYGBgCgoKKipMYSBzdW1hIGFsZ2VicmFpY2EgZGUgbG9zIHJlc2lkdWFsZXMgc2llbXByZSBlcyBjZXJvLioqCgoKCjxicj4KCiMjIFN1bWEgQ3VhZHJhZGEgZGVsIEVycm9yCgpTZSBlbGV2YXLDoSBlbCB2YWxvciBkZSBsb3MgcmVzaWR1YWxlcyBhbCBjdWFkcmFkbywgY29tbyBsbyBtdWVzdHJhIGxhIHNpZ3VpZW50ZSB0YWJsYSwgY29uIGVsIG9iamV0aXZvIGRlIG9idGVuZXIgdmFsb3JlcyBwb3NpdGl2b3MgZGUgbG9zIG1pc21vcywgZGUgbGEgbWlzbWEgbWFuZXJhIHF1ZSBlbiBsYSBkZXN2aWFjacOzbiBlc3TDoW5kYXIuCgokJERlcy5Fc3QgPSBcc3FydHtcZnJhY3tcc3VtKHgtXGJhcnt4fSleMn17bn19JCQKCmBgYHtyfQpkYXRhLmZyYW1lKHBsYXRvID0gcHJvcGluYXMkcGxhdG8sCiAgICAgICAgICAgcmVzaWR1YWxlcyA9IHJlc2lkdWFsZXMsCiAgICAgICAgICAgInJlc2lkXjIiID0gKHJlc2lkdWFsZXNeMikpCmBgYAoKTGEgc3VtYSBkZSBlc3RvcyByZXNpZHVhbGVzIHF1ZSBlc3RhbiBlbGV2YWRvcyBhbCBjdWFkcmFkbywgc2UgY29ub2NlIGNvbW8gbGEgKlN1bWEgZGUgQ3VhZHJhZG9zIGRlbCBFcnJvciogbyBsYSAqU3VtYSBkZSBDdWFkcmFkb3MgZGUgbG9zIFJlc2lkdWFsZXMqIChTQ0UpLiBFbiBpbmdsw6lzICBpbmdsw6lzIFN1bSBvZiBTcXVhcmVzIEVycm9yIChTU0UpLgoKT2J0ZW5pZW5kbyBsYSBzdW1hIGRlIGN1YWRyYWRvcyBkZWwgZXJyb3I6CgokJFNDRSA9IDI1KzQ5KzErNCsxNisyNT0xMjAkJApgYGB7cn0Kc3VtKHJlc2lkdWFsZXNeMikKYGBgCiFbXShjdWFkcmFkb3NfZGVsX2Vycm9yLnBuZykKCjxicj4KCiMgT2JqZXRpdm8KCkVsIG9iamV0aXZvIGRlIGxhIHJlZ3Jlc2nDs24gbGluZWFsIGVzIGNyZWFyIHVuIG1vZGVsbyBsaW5lYWwgcXVlICptaW5pbWljZSBsYSBzdW1hIGRlIGN1YWRyYWRvcyBkZWwgZXJyb3IqLiAKClN1IHByb3DDs3NpdG8gcHJpbmNpcGFsIGVzIGVzdGltYXIgbGEgZnVuY2nDs24gZGUgcmVncmVzacOzbiBwb2JsYWNpb25hbCBjb24gYmFzZSBlbiBsYSBmdW5jacOzbiBkZSByZWdyZXNpw7NuIG11ZXN0cmFsLgoKRGljaG8gZGUgb3RyYSBtYW5lcmEsIGVsIG9iamV0aXZvIGVzIG9idGVuZXIgdW4gbW9kZWxvIChsw61uZWEgZGUgYWp1c3RlKSAqYWdyZWdhbmRvIHVuYSB2YXJpYWJsZSBpbmRlcGVuZGllbnRlKiBxdWUgbWluaW1pY2UgZWwgdGFtYcOxbyBkZSBsb3MgY3VhZHJhZG9zIHRhbnRvIGNvbW8gc2VhIHBvc2libGUuIAoKU2Ugb2J0aWVuZSB1bmEgbMOtbmVhIHF1ZSBzZSBhanVzdGEgbWVqb3IgYSBsb3MgZGF0b3MgdG9tYWRvcyBkZWwgcHJvYmxlbWEgeSBxdWUgbWltaW5pemEgbG9zIHJlc2lkdWFsZXMuICAKCgojIEVzdGFkw61zdGljYSBCaXZhcmlhZGEKCgpFbCBlc3R1ZGlvIGRlbCBhbsOhbGlzaXMgZGUgdmFyaWFuemEgQU5PVkEgeSBkZSBsYSBjb3JyZWxhY2nDs24sIHNlIHJlbGFjaW9uYW4gY29uIGxhIHJlZ3Jlc2nDs24gbGluZWFsIHNpbXBsZS4gIAoKCiFbXShsaW5lYXJfcmVncmVzc2lvbi5wbmcpCgo8YnI+CgojIyAiUmVwYXNvIGRlIMOBbGdlYnJhIjogTMOtbmVhIFJlY3RhCgohW10ocmVjdGEucG5nKQoKPGJyPgoKIyBNb2RlbG8gZGUgUmVncmVzacOzbiBMaW5lYWwKClBhcmEgZWwgbW9kZWxvIGRlIHJlZ3Jlc2nDs24gbGluZWFsIHNlIGhhY2UgbmVjZXNhcmlvIGxhIGV4aXN0ZW5jaWEgZGUgZG9zIHZhcmlhYmxlcywgYSBkaWZlcmVuY2lhIGRlbCBjYXNvIGFudGVyaW9yIGVuIGVsIGN1YWwgc8OzbG8gc2UgY29udGFiYSBjb24gaW5mb3JtYWNpw7NuIGRlIHVuYSBzb2xhIHZhcmlhYmxlIChwcm9waW5hKS4gIAoKKiBNb2RlbG8gbWF0ZW3DoXRpY28KCiQkeSA9IG14K2IkJApFbCBtb2RlbG8gZGUgcmVncmVzacOzbiBsaW5lYWwgcGFyYSBsYSBwb2JsYWNpw7NuIGRlIGRhdG9zIHF1ZSBzZSBjb25zaWRlcmUgZXN0w6EgZGFkYSBwb3I6CgoqIE1vZGVsbyBlc3RhZMOtc3RpY28KCiQkeSA9IFxiZXRhX3swfStcYmV0YV97MX14JCQgCgokJFxoYXR7eX0gPSBcaGF0e1xiZXRhXzB9K1xoYXRcYmV0YV97MX1YX2krXGVwc2lsb24kJAoKRW4gZXNlbmNpYSBlcyBpZ3VhbCBhIGxhIHJlY3RhIHBlbmRpZW50ZSBpbnRlcmNlcHRvIGRlIGFycmliYS4gIAoKKiAkXGJldGFfezB9ID0kIGludGVyY2VwdG8gY29uIGVsIGVqZSAkeSQgZGVsIHBhcsOhbWV0cm8gZGUgbGEgcG9ibGFjacOzbi4gIAoKCiogJFxiZXRhX3sxfT0kIHBlbmRpZW50ZSBkZWwgcGFyw6FtZXRybyBkZSBsYSBwb2JsYWNpw7NuLiAgCgoKKiAkXGVwc2lsb249JCB0w6lybWlubyBwYXJhIGVsIGVycm9yLCB2YXJpYWNpw7NuIHF1ZSBubyBzZSBwdWVkZSBleHBsaWNhciBkZSBsYSB2YXJpYWJsZSAkeSQuICAKCgpMYSBmdW5jacOzbiBvIGVjdWFjacOzbiBxdWUgZGVzY3JpYmUgbGEgcmVncmVzacOzbiBsaW5lYWwgc2ltcGxlIGVzOgoKJCRFKHkpPSBcYmV0YV97MH0rXGJldGFfezF9eCQkCkVsIHZhbG9yIGVzcGVyYWRvIGRlICR5JCBlcyBsYSBtZWRpYSwgcGFyYSB1biB2YWxvciBkYWRvIGRlICR4JC4KCkVsIHZhbG9yIGVzcGVyYWRvIGRlICR5JCBlcyByZWFsbWVudGUgbGEgbWVkaWEgZGUgdW5hIGRpc3RyaWJ1Y2nDs24gYWxyZWRlZG9yIGRlIGxvcyB2YWxvcmVzIGRlICR5JC4KCiFbXShsaW5lYXNfcmVncmVzaW9uLnBuZykKPGJyPgoKQSBtYW5lcmEgZGUgZWplbXBsbyB5IHRvbWFuZG8gbGEgYmFzZSBkZSBkYXRvcyBgY2Fyc2AgZGUgUiwgc2UgcHJlc2VudGEgZWwgbW9kZWxvIGRlIHJlZ3Jlc2nDs24gbGluZWFsIHBhcmEgZWwgY29uanVudG8gZGUgZGF0b3M6CgpgYGB7cn0KbGlicmFyeShnZ3BtaXNjKQpjYXJzICU+JSAKICBnZ3Bsb3QobWFwcGluZyA9IGFlcyh4ID0gZGlzdCwgeSA9IHNwZWVkKSkrCiAgZ2VvbV9wb2ludCgpKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlID0gRikrCiAgbGFicyh0aXRsZSA9ICJMw61uZWEgZGUgUmVncmVzacOzbiIsIHkgPSAiRGlzdGFuY2lhIiwgeCA9ICJWZWxvY2lkYWQiLCBzdWJ0aXRsZSA9ICJCYXNlIGRlIERhdG9zIGNhcnMiKSsKICBzdGF0X3BvbHlfZXEoYWVzKGxhYmVsID0gcGFzdGUoLi5lcS5sYWJlbC4uICwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLi5yci5sYWJlbC4uICwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VwID0gIn5+fn4iKSksCiAgICAgICAgICAgICAgIGZvcm11bGEgPSB5IH4geCwgcGFyc2UgPSBUUlVFLAogICAgICAgICAgICAgICBsYWJlbC54Lm5wYyA9IDAuNSwKICAgICAgICAgICAgICAgY29sb3IgPSAiYmx1ZSIpCmBgYAoKCiMgRWN1YWNpw7NuIGRlIHJlZ3Jlc2nDs24gY29uIGVzdGltYWNpb25lcwoKU2kgc2UgY29ub2NlbiBsb3MgdmFsb3JlcyBwYXJhIGxvcyBwYXLDoW1ldHJvcyAkXGJldGFfezB9JCB5ICRcYmV0YV97MX0kLCBlcyBwb3NpYmxlIHVzYXIgbGEgZWN1YWNpw7NuIGRlIHJlZ3Jlc2nDs24gbGluZWFsIHNpbXBsZTogJCRFKHkpPVxiZXRhX3swfStcYmV0YV97MX14JCQKClBlcm8gZW4gcmVhbGlkYWQgY2FzaSBudW5jYSBzZSBjb25vY2VuIGxvcyB2YWxvcmVzIGRlIGxvcyBwYXLDoW1ldHJvcyBkZSBsYSBwb2JsYWNpw7NuLCBwb3IgbG8gdGFudG8gc2UgZGViZW4gZXN0aW1hciB1c2FuZG8gdW5hIG11ZXN0cmEgZGUgbG9zIGRhdG9zLgoKQ3VhbmRvIHVzYW1vcyB1bmEgbXVlc3RyYSBkZSBsb3MgZGF0b3MgbGEgZWN1YWNpw7NuIHNlIGVzY3JpYmUgY29tbzoKCiQkXGhhdHt5fT0gYl97MH0rYl97MX14JCQKCmRvbmRlICRcaGF0e3l9JCBlcyBlbCBlc3RpbWFkb3IgcGFyYSBlbCB2YWxvciBlc3BlcmFkbyBkZSAkeSQuCgokRSh5KSQgZXMgbGEgbWVkaWEgZGVsIHZhbG9yICR5JCBwYXJhIHVuIHZhbG9yIGRhZG8gZGUgJHgkLgoKPGJyPgoKIyMgUGVyY2VwY2nDs24gR2VvbcOpdHJpY2EKCiFbXShwZGljZS5wbmcpCgo8YnI+Cgo8YnI+CgohW10oaW5kaWNlLmdpZikKCjxicj4KCj4gTk9UQTogZW4gbGEgbGl0ZXJhdHVyYSwgZW4gdmFyaW9zIGNhc29zIGxhIGRpZmVyZW5jaWEgZW50cmUgbGFzIGVjdWFjaW9uZXMsIHNlIHJlZmxlamEgZW4gZWwgdXNvIGRlIGxhcyBsZXRyYXMgYiBtaW7DunNjdWxhcywgcGFyYSByZW1hcmNhciBxdWUgc2UgdHJhYmFqYSBjb24gdW5hIG11ZXN0cmEgZGUgbGEgcG9ibGFjacOzbi4KCjxicj4KCkFwbGljYWRvIGFsIGVqZW1wbG8gZW4gZWwgY3VhbCBzw7NsbyBzZSB0aWVuZSBpbmZvcm1hY2nDs24gZGUgdW5hIHZhcmlhYmxlLCBsYSBwcm9waW5hLCBlbCB2YWxvciBkZSBsYSBwZW5kaWVudGUgc2Vyw61hIGNlcm8sIHlhIHF1ZSBkZSBoZWNobyBsYSB2YXJpYWJsZSAkeCQgbm8gZXhpc3RlLCBwb3IgbG8gdGFudG86CgohW10ocGVuZGllbnRlX2Nlcm8ucG5nKQoKPGJyPgoKRWwgdmFsb3IgZGUgJFxoYXR7eX0kIGVzICQxMCQgcGFyYSB0b2RvIHZhbG9yIGRlICR4JC4KClNpZ3VpZW5kbyBjb24gZWwgbWlzbW8gZWplbXBsbyBkZWwgcmVzdGF1cmFudGUgc2kgc2UgYWdyZWdhIGxhIGluZm9ybWFjacOzbiBkZWwgdG90YWwgZGUgbGEgZmFjdHVyYSB5IGVsIHZhbG9yIGRlIGxhIHByb3BpbmE7IGVuIGVzdGUgY2FzbyBzZSBkaWNlIHF1ZSBlbCB2YWxvciBkZSBsYSBwcm9waW5hIGRlcGVuZGUgZGVsIG1vbnRvIHRvdGFsIGRlIGxhIGZhY3R1cmEgKFVTJCkuCgpBZ3JlZ2FuZG8gdW5hIG51ZXZhIGNvbHVtbmEgYSBsb3MgZGF0b3M6CgpgYGB7cn0KcHJvcGluYXMkZmFjdHVyYSA8LSBjKDM0LCAxMDgsIDY0LCA4OCwgOTksIDUxKQoKcHJvcGluYXMKYGBgCgpIaXBvdMOpdGljYW1lbnRlIHNlIGRpY2UgcXVlIGVsIHZhbG9yIGRlIGxhIHByb3BpbmEgZGVwZW5kZXLDoSBkZWwgdmFsb3IgdG90YWwgZGUgbGEgZmFjdHVyYSwgdW5hIGZhY3R1cmEgcG9yIHVuIHZhbG9yIGJham8gZGFyw6EgY29tbyByZXN1bHRhZG8gdW5hIHByb3BpbmEgbcOhcyBiYWphIGNvbXBhcmFkYSBjb24gZWwgbW9udG8gZGUgdW5hIHByb3BpbmEgZGVqYWRhIHBvciB1bmEgZmFjdHVyYSBkZSBtw6FzIGFsdG8gdmFsb3IuICAKClNpIGxhIGzDrW5lYSBkZSB0ZW5kZW5jaWEgcmVzdWx0YW50ZSBvYnNlcnZhZGEsIHJlZHVjZSBsYSBzdW1hIGRlIGN1YWRyYWRvcyBkZWwgZXJyb3IsIG9idGVuaWRhIGVuIGVsIGNhc28gZW4gZWwgcXVlIHNlIHRlbsOtYSBzw7NsbyBsYSB2YXJpYWJsZSBwcm9waW5hcyBxdWUgdXNhYmEgZWwgbW9kZWxvIGRlIGxhIG1lZGlhIGRlICQxMCBVU1wkJCwgc2UgZGljZSBxdWUgZWwgbW9kZWxvIGRlIHJlZ3Jlc2nDs24gZXMgbWVqb3IgY3VhbGl0YXRpdmFtZW50ZSBjb21wYXJhZG8gY29uIGVsIG1vZGVsbyBkZSBsYSBtZWRpYS4KClNlIHBvZHLDoSBjYWxjdWxhciBkZSBtYW5lcmEgY3VhbnRpdGF0aXZhIGN1w6FudG8gZXMgbWVqb3IgdW4gbW9kZWxvIGVuIGNvbXBhcmFjacOzbiBjb24gb3Ryby4gIAoKCiMgTcOpdG9kbyBkZSBNw61uaW1vcyBDdWFkcmFkb3MKCkFob3JhIHF1ZSBzZSB0aWVuZSBsYSBpbmZvcm1hY2nDs24gZW4gcGFyZXMsIHNvYnJlIGVsIHZhbG9yIHRvdGFsIGRlIGxhIGZhY3R1cmEgeSBsYSBwcm9waW5hIGRlamFkYSBkZSBlc2EgZmFjdHVyYTsgc2UgZGVzZWEgY29ub2NlciBlbiBxdcOpIGdyYWRvIGVsIG1vbnRvIGRlIGxhIHByb3BpbmEgcHVlZGUgc2VyIHByZWRpY2hhIHBvciBlbCB2YWxvciB0b3RhbCBkZSBsYSBmYWN0dXJhLgoKU2UgcHVlZGUgYWZpcm1hciBlbnRvbmNlcyBxdWUgbGEgdmFyaWFibGUgKnByb3BpbmEqIGVzIGxhIHZhcmlhYmxlIGRlcGVuZGllbnRlIHkgbGEgdmFyaWFibGUgaW5kZXBlbmRpZW50ZSBlcyBsYSAqZmFjdHVyYSouICAKCkVzdGUgcGxhbnRlYW1pZW50byBlcyBpbXBvcnRhbnRlIGhhY2VybG8gcGFyYSBkZXRlcm1pbmFyIGVsIHJvbCBkZSBsYXMgdmFyaWFibGVzIGVuIG51ZXN0cm8gbW9kZWxvLCBubyB0aWVuZSBzZW50aWRvIGFsZ3VubyBhZmlybWFyIHF1ZSBlbCB2YWxvciB0b3RhbCBkZSBsYSBmYWN0dXJhIGVzdMOhIGRhZG8gcG9yIGVsIG1vbnRvIGRlIGxhIHByb3BpbmEuICAKCgojIyBDcml0ZXJpbyBkZSBNw61uaW1vcyBDdWFkcmFkb3MKCiQkbWluXHN1bSh5X3tpfS1caGF0e3l9X2kpXjIkJCAgCgoqICR5X3tpfT0kIGVzIGVsIHZhbG9yIG9ic2VydmFkbyBkZSBsYSB2YXJpYWJsZSBkZXBlbmRpZW50ZSAocHJvcGluYSkuCgoqICRcaGF0e3l9X2k9JCB2YWxvciBlc3RpbWFkbyAocHJlZGljaG8pIGRlIGxhIHZhcmlhYmxlIGRlcGVuZGllbnRlICh2YWxvciBwcmVkaWNobyBkZSBsYSBwcm9waW5hKS4gQmFzYWRvIGVuIHVuIG1vZGVsbyBkZSByZWdyZXNpw7NuLgoKClNlIHRlbmRyw6FuIGRvcyB2YWxvcmVzIHBhcmEgY2FkYSB2YWxvciBkZSAkeCQsIGVsIHZhbG9yIG9ic2VydmFkbyAocmVhbCkgeSBlbCB2YWxvciBwcmVkaWNoby4gIAoKRWwgb2JqZXRpdm8gZXMgbWluaW1pemFyIGxhIHN1bWEgZGUgbGFzIGRpZmVyZW5jaWFzIGN1YWRyYWRhcyBlbnRyZSBlbCB2YWxvciBvYnNlcnZhZG8gcGFyYSBsYSB2YXJpYWJsZSBkZXBlbmRpZW50ZSAoJHlfaSQpIHkgZWwgdmFsb3IgcHJlZGljaG8gbyBlc3RpbWFkbyBkZSBsYSB2YXJpYWJsZSBkZXBlbmRpZW50ZSAoJFxoYXR7eX1faSQpIHF1ZSBlc3RhIGRhZG8gcG9yIGxhIGzDrW5lYSBkZSByZWdyZXNpw7NuLiBTdW1hIGRlIGxvcyBjdWFkcmFkb3MgZGVsIGVycm9yLiAgCgpFbnRvbmNlcyBsYSBzdW1hIGRlIGxvcyBjdWFkcmFkb3MgcmVzaWR1YWxlcyBkZWJlIHNlciBtdWNobyBtZW5vciBxdWUgbGEgb2J0ZW5pZGEgc8OzbG8gY29uIGxhIHZhcmlhYmxlIGRlcGVuZGllbnRlOyAkXGJldGFfMT0wJCwgJFxoYXR7eX09MTAkIHBhcmEgY2FkYSB2YWxvciBkZSAkeCQuIExhIHN1bWEgZGUgbG9zIGN1YWRyYWRvcyBkZWJlIHNlciBtdWNobyBtZW5vciBxdWUgJDEyMCQgcXVlIGZ1w6kgbGEgb2J0ZW5pZGEuICAKCgojIyBHcsOhZmljbyBkZSBkaXNwZXJzacOzbgoKVW4gZ3LDoWZpY28gZGUgZGlzcGVyc2nDs24gYXJyb2phIGluZm9ybWFjacOzbiBhY2VyY2EgZGUgZWwgY29tcG9ydGFtaWVudG8gcXVlIHNpZ3VlbiBsb3MgZGF0b3MsIGhhY2VyIHVzbyBkZSB1bmEgZXNjYWxhIGFjb3JkZSBhIGxvcyBkYXRvcywgZmFjaWxpdGEgbGEgaW50ZXJwcmV0YWNpw7NuIGRlbCBncsOhZmljby4gIAoKSW5maW5pdGFzIHJlY3RhcyBwdWVkZW4gYWp1c3RhcnNlIGVuIG1lbm9yIG8gbWF5b3IgbWVkaWRhIGEgbG9zIGRhdG9zLCBsYSBpZGVhIGVzIG9idGVuZXIgYXF1ZWxsYSBxdWUgbWluaW1pY2UgZW50b25jZXMsIGxhIHN1bWEgZGUgbG9zIGN1YWRyYWRvcyBkZWwgZXJyb3IuCgpFbiBjb21wYXJhY2nDs24gY29uIGxvcyByZXNpZHVhbGVzIG9idGVuaWRvcyBjb24gbGEgbWVkaWEgZGUgbGFzIHByb3BpbmFzIChsw61uZWEgaG9yaXpvbmFsKSwgbGEgc3VtYSBkZSBsb3MgY3VhZHJhZG9zIHJlc2lkdWFsZXMgZGUgbGFzIHJlY3RhcyBjb24gcGVuZGllbnRlIGRpZmVyZW50ZSBhIGNlcm8sIGVzIG1lbm9yLgoKYGBge3J9CnByb3BpbmFzICU+JSAKICBnZ3Bsb3QobWFwcGluZyA9IGFlcyh4ID0gZmFjdHVyYSwgeSA9IHByb3BpbmEpKSsKICBnZW9tX3BvaW50KGNvbG9yID0gImJsdWUiKSsKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZSA9IEZBTFNFLCBjb2xvciA9ICJvcmFuZ2UiLCBsaW5ldHlwZSA9ICJkYXNoZWQiKSsKICBsYWJzKHggPSAiRmFjdHVyYSIsCiAgICAgICB5ID0gIlByb3BpbmEiLAogICAgICAgdGl0bGUgPSAiUG9zaWJsZXMgTMOtbmVhcyBkZSBSZWdyZXNpw7NuIikrCiAgZ2VvbV9hYmxpbmUoc2xvcGUgPSAwLjE0NiwKICAgICAgICAgICAgICBpbnRlcmNlcHQgPSAxLAogICAgICAgICAgICAgIGxpbmV0eXBlID0gImRhc2hlZCIsCiAgICAgICAgICAgICAgY29sb3IgPSAiZm9yZXN0Z3JlZW4iLAogICAgICAgICAgICAgIHNpemUgPSAxKSsKICBnZW9tX2FibGluZShzbG9wZSA9IDAuMTQ2LAogICAgICAgICAgICAgIGludGVyY2VwdCA9IC0yLAogICAgICAgICAgICAgIGxpbmV0eXBlID0gImRhc2hlZCIsCiAgICAgICAgICAgICAgY29sb3IgPSAicHVycGxlIiwKICAgICAgICAgICAgICBzaXplID0gMSkrCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbD1wcm9waW5hKSxoanVzdD0tMC41LCB2anVzdD0wKSsKICB0aGVtZV9idygpCmBgYAoKCiMjIENvcnJlbGFjacOzbgoKRWwgY29lZmljaWVudGUgZGUgY29ycmVsYWNpw7NuIGRlIFBlYXJzb24gZXMgdW5hIG1lZGlkYSBsaW5lYWwgZW50cmUgZG9zIHZhcmlhYmxlcyBhbGVhdG9yaWFzIGN1YW50aXRhdGl2YXMuIEEgZGlmZXJlbmNpYSBkZSBsYSBjb3ZhcmlhbnphLCBsYSBjb3JyZWxhY2nDs24gZXMgaW5kZXBlbmRpZW50ZSBkZSBsYSBlc2NhbGEgZGUgbWVkaWRhLgoKRXN0ZSBjb2VmaWNpZW50ZSBwdWVkZSBzZXIgZGUgZG9zIHRpcG9zOgoKKiBQYXJhbcOpdHJpY286IHN1amV0byBhIGRpc3RyaWJ1Y2nDs24gbm9ybWFsIG8gZ2F1c2lhbmEuCgoqIE5vIHBhcmFtw6l0cmljbzogbm8gZXN0w6Egc3VqZXRvIGEgZGlzdHJpYnVjacOzbiBub3JtYWwgbyBnYXVzaWFuYS4KCsOadGlsIHBhcmEgb2J0ZW5lciBlbCBjb2VmaWNpZW50ZSBkZSBjb3JyZWxhY2nDs24gZGUgIGVsIG1vZGVsbyBkZSByZWdyZXNpw7NuIGxpbmVhbC4gIAoKJCRyKHgseSkgPSBcZnJhY3tDb3Zfe3gseX19e1xzaWdtYV94IFxzaWdtYV95fSAgPSBcZnJhY3tcc3VtKHhfaSAtIFxiYXJ7eH0pKHlfaSAtIFxiYXJ7eX0pfXtcc3FydHtcc3VtKHhfaSAtIFxiYXJ7eH0pXjJ9KlxzcXJ0e1xzdW0oeV9pIC0gXGJhcnt5fSleMn19JCQKPGJyPgoKIVtdKGNvcnJlbC5qcGVnKQo8YnI+CgohW10oZnVlcnRlZGViaWwucG5nKQoKPGJyPgoKRWwgY29lZmljaWVudGUgZGUgY29ycmVsYWNpw7NuICAkciQgcGVybWl0ZSB2ZXJpZmljYXIgc2kgbGEgcmVsYWNpw7NuIG9idGVuaWRhIGVuIGxvcyBkYXRvcyBlcyBsaW5lYWwsIHBvc2l0aXZhIG8gbmVnYXRpdmE7IHRhbWJpw6luIGluZGljYSBxdWUgbm8gaGF5IGNvcnJlbGFjacOzbiBsaW5lYWwsIGVuIGNhc28gZGUgc2VyIGNlcm8uIEVudHJlIG1hcyBjZXJjYW5vIGVzIGEgMSBvIC0xIGVzIG1hcyBmdWVydGUsIGVudHJlIG1hcyBjZXJjYW5vIGEgMCBlcyBkw6liaWwgaGFzdGEgbGxlZ2FyIGhhY2Vyc2UgbnVsYS4KCgoqIENvcnJlbGFjacOzbiBwZXJmZWN0YSBuZWdhdGl2YSA9ICQtMSQgLgoqIE5vIGV4aXN0ZSBjb3JyZWxhY2nDs24gPSAkMCQuCiogQ29ycmVsYWNpw7NuIHBlcmZlY3RhIHBvc2l0aXZhID0gJDEkLgoKCkNhbGN1bGFuZG8gbGEgY29ycmVsYWNpw7NuIGRlIGxhcyB2YXJpYWJsZXMgY29uIFI6CgpgYGB7cn0KY29yKHByb3BpbmFzJHByb3BpbmEsIHByb3BpbmFzJGZhY3R1cmEpCmBgYAoKVGFtYmnDqW4gc2UgcHVlZGUgb2J0ZW5lciBsYSBtYXRyaXogZGUgY29ycmVsYWNpw7NuIGRlIGxhcyB2YXJpYWJsZXMgZGVsIGNvbmp1bnRvIGRlIGRhdG9zOgoKYGBge3J9CmNvcihwcm9waW5hcykKYGBgCgoKIyMgRXN0YWTDrXN0aWNhIERlc2NyaXB0aXZhIGRlIGxhcyBWYXJpYWJsZXMKCkNvbm9jZXIgbGFzIHZhcmlhYmxlcyBkZSBtYW5lcmEgZ8WVYWZpY2EgeSBudW3DqXJpY2EsIGVzIHVuYSBidWVuYSBwcsOhY3RpY2EgcXVlIGFycm9qYSBpbmZvcm1hY2nDs24gaW1wb3J0YW50ZSBwYXJhIHZhbGlkYXIgbGFzIGNvbmRpY2lvbmVzIHF1ZSBwZXJtaXRhbiBsYSBhcGxpY2FjacOzbiBkZWwgbW9kZWxvLgoKT2J0ZW5lciBsYXMgbWVkaWFzIHBhcmEgY2FkYSB1bmEgZGUgbGFzIHZhcmlhYmxlczoKCiogJFxiYXJ7eH0kID0gJDc0JCAodmFsb3IgcHJvbWVkaW8gZmFjdHVyYSkgCgoqICRcYmFye3l9JCA9ICQxMCQgKHZhbG9yIHByb21lZGlvIHByb3BpbmEpCgpgYGB7cn0KbWVhbihwcm9waW5hcyRmYWN0dXJhKQpgYGAKCmBgYHtyfQptZWFuKHByb3BpbmFzJHByb3BpbmEpCmBgYApFbCBwdW50byBxdWUgc2UgdWJpY2EgZW4gbGEgY29vcmRlbmFkYSAkXGJhcnt4fSA9IDc0JCAsICRcYmFye3l9ID0gMTAkLCBzZSBjb25vY2UgY29tbyAqY2VudHJvaWRlKiwgeSBlcyBlbCBwdW50byBmb3JtYWRvIHBvciBsYXMgbWVkaWFzIGRlIGxhcyB2YXJpYWJsZXMuCgpMYSBsw61uZWEgZGUgcmVncmVzacOzbiBxdWUgbWVqb3Igc2UgYWp1c3RlIHBhc2Fyw6EgcG9yIGVsIGNlbnRyb2lkZS4gIAoKClJlc3VtZW4gdGFidWxhciBkZSBsb3MgdmFsb3JlcyBudW3DqXJpY29zIG5lY2VzYXJpb3MgcGFyYSBlbCBtb2RlbG8gZGUgcmVncmVzacOzbiBsaW5lYWw6CgpgYGB7cn0KcHJvcGluYXMgJT4lIAogIG11dGF0ZShkZXN2cHJvcCA9IHByb3BpbmEtbWVhbihwcm9waW5hKSwKICAgICAgICAgZGVzdmZhY3QgPSBmYWN0dXJhLW1lYW4oZmFjdHVyYSksCiAgICAgICAgIHByb2RkZXN2ID0gZGVzdmZhY3QqZGVzdnByb3AsCiAgICAgICAgIGN1YWRlc3ZwID0gZGVzdnByb3BeMiwKICAgICAgICAgY3VhZGVzdmYgPSBkZXN2ZmFjdF4yKQogICAgICAgICAKYGBgCgo8YnI+CgpPdHJvcyB2YWxvcmVzIG51bcOpcmljb3MgYmFzdGFudGUgw7p0aWxlcyBzb246CgoqIE1lZGlhbmEuCgpgYGB7cn0KbWVkaWFuKHByb3BpbmFzJGZhY3R1cmEpCmBgYAoKCiogRGVzdmlhY2nDs24gRXN0w6FuZGFyLgoKYGBge3J9CnNkKHByb3BpbmFzJGZhY3R1cmEpCmBgYAoKCiogVmFyaWFuemEKCmBgYHtyfQp2YXIocHJvcGluYXMkZmFjdHVyYSkKYGBgCgoKKiBDb3ZhcmlhbnphCgpgYGB7cn0KY292KHByb3BpbmFzJHByb3BpbmEsIHByb3BpbmFzJGZhY3R1cmEpCmBgYAoKIyBPYnRlbmVyIGxhIEzDrW5lYSBkZSBSZWdyZXNpw7NuCgokJFxoYXR7eX1faT1iX3swfStiX3sxfXhfe2l9JCQgIAoKClBlbmRpZW50ZToKJCRiX3sxfT1cZnJhY3tcc3VtKHhfe2l9LVxiYXJ7eH0pKHlfe2l9LVxiYXJ7eX0pfXtcc3VtKHhfe2l9LVxiYXJ7eH0pXjJ9ID0gMC4xNDYyJCQgIAoKZG9uZGUsCgoqICRcYmFye3h9PSQgbWVkaWEgZGUgbGEgdmFyaWFibGUgaW5kZXBlbmRpZW50ZS4gIAoKKiAkXGJhcnt5fT0kIG1lZGlhIGRlIGxhIHZhcmlhYmxlIGRlcGVuZGllbnRlLiAgCgoqICR4PSQgdmFsb3IgZGUgbGEgdmFyaWFibGUgaW5kZXBlbmRpZW50ZS4gIAoKKiAkeT0kIHZhbG9yIGRlIGxhIHZhcmlhYmxlIGRlcGVuZGllbnRlLiAgCgo8YnI+CgpJbnRlcmNlcHRvOgoKJCRiX3swfT0gXGJhcnt5fS1iX3sxfVxiYXJ7eH0gPSAtMC44MTg4JCQKCkVudG9uY2VzLCBsYSBmw7NybXVsYSBxdWUgZGVzY3JpYmUgbGEgbMOtbmVhIGRlIHJlZ3Jlc2nDs24gbGluZWFsIGVuIHTDqXJtaW5vcyBkZSBsYXMgdmFyaWFibGVzIGVzOgoKJCRwcm9waW5hID0gLTAuODE4OCsoMC4xNDYyKWZhY3R1cmEkJAoKUGFyYSBvYnRlbmVyIGxhIHJlcHJlc2VudGFjacOzbiBnZW9tw6l0cmljYSBkZSBsYSBsw61uZWEgZGUgcmVncmVzacOzbiBjb24gUiwgaGFjZW1vcyB1c28gZGUgZWwgbcOpdG9kbyBgbG1gLgoKYGBge3J9CnByb3BpbmFzICU+JSAKICBnZ3Bsb3QobWFwcGluZyA9IGFlcyh4ID0gZmFjdHVyYSwgeSA9IHByb3BpbmEpKSsKICBnZW9tX3BvaW50KGNvbG9yID0gImNhZGV0Ymx1ZSIpKwogIGdlb21fdGV4dChhZXMobGFiZWw9cHJvcGluYSksaGp1c3Q9MCwgdmp1c3Q9MCkrCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2UgPSBGQUxTRSwgY29sb3IgPSAiZmlyZWJyaWNrMiIpKwogIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyh4ID0gNzQsIHkgPSAxMCksIGNvbG9yID0gImJsdWUiLCBzaXplID0gMikrCiAgYW5ub3RhdGUoInRleHQiLAogICAgICAgICAgIGxhYmVsID0gIkNlbnRyb2lkZSIsCiAgICAgICAgICAgeCA9IDgzLAogICAgICAgICAgIHkgPSAxMCwKICAgICAgICAgICBjb2xvciA9ICJibHVlIikrCiAgbGFicyh4ID0gIkZhY3R1cmEiLAogICAgICAgeSA9ICJQcm9waW5hIiwKICAgICAgIHRpdGxlID0gIkzDrW5lYSBkZSBSZWdyZXNpw7NuIikrCiAgIHN0YXRfcG9seV9lcShhZXMobGFiZWwgPSBwYXN0ZSguLmVxLmxhYmVsLi4gLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZXAgPSAifn5+fiIpKSwKICAgICAgICAgICAgICAgZm9ybXVsYSA9IHkgfiB4LCBwYXJzZSA9IFRSVUUsCiAgICAgICAgICAgICAgIGxhYmVsLngubnBjID0gMC41LAogICAgICAgICAgICAgICBjb2xvciA9ICJmaXJlYnJpY2siKSsKICB0aGVtZV9idygpCmBgYAoKPGJyPgoKCgojIEludGVycHJldGFjacOzbgoKJCRcaGF0e3lfaX0gPSAwLjE0NjJ4LTAuODE4OCQkCgpFbCB2YWxvciBkZSBsYSBwZW5kaWVudGUgcGFyYSBlc3RlIGNhc28sIHNpZ25pZmljYSwgcXVlIHBvciBjYWRhIGTDs2xhciBxdWUgYXVtZW50YSBlbCB2YWxvciBkZSBsYSBmYWN0dXJhLCBlc3BlcmFyw61hbW9zIHF1ZSBlbCBtb250byBwYXJhIGxhIHByb3BpbmEgYXVtZW50ZSBlbiAkMC4xNDYyJCAkVVNcJCQsIGFwcm94aW1hZGFtZW50ZSAkMTUkIGNlbnRhdm9zIGRlIGTDs2xhci4KCkxhIGludGVycHJldGFjacOzbiBkZWwgaW50ZXJjZXB0bywgcHVlZGUgbyBuw7MsIHRlbmVyIHVuIHNpZ25pZmljYWRvIGVuIGVsICJtdW5kbyByZWFsIiwgZGVwZW5kZXLDoSBlbnRvbmNlcyBkZSBlbCBmZW7Ds21lbm8gcXVlIHNlIGVzdMOpIGVzdHVkaWFuZG8uIEVuIGVzdGUgY2FzbyBlbiBwYXJ0aWN1bGFyLCBzaSBlbCB2YWxvciBkZSBsYSBmYWN0dXJhIGVzIGNlcm8sIGVsIG1vZGVsbyBwcmVkaWNlIHVuIHZhbG9yIGRlIHByb3BpbmEgZGUgJC0wLjgxODgkICRVU1wkJCwgbG8gY3VhbCBubyB0aWVuZSBzZW50aWRvIGVuIGxhIHZpZGEgcmVhbCB5YSBzZSBvYnRpZW5lIHVuIHZhbG9yIG5lZ2F0aXZvIGRlIHByb3BpbmEuCgpFcyBpbXBvcnRhbnRlIGFub3RhciwgcXVlIGVsIG1vZGVsbyBkZSByZWdyZXNpw7NuIGVzIMO6bmljbyBwYXJhIGVsIGNvbmp1bnRvIGRlIGRhdG9zIHF1ZSByZXByZXNlbnRhOyBhZGljaW9uYXIgbyBjYW1iaWFyIGRhdG9zIGdlbmVyYXLDoSB1biBjYW1iaW8gZW4gZWwgbW9kZWxvLgoKCgojIEFqdXN0ZSB5IENvZWZpY2llbnRlIGRlIERldGVybWluYWNpw7NuCgrCv1F1w6kgdGFuIGJpZW4gc2UgYWp1c3RhIGxhIGzDrW5lYSBkZSByZWdyZXNpw7NuIGEgbG9zIGRhdG9zLCBjb21wYXJhZG8gY3VhbmRvIHNlIHVzYSBzw7NsbyBlbCBwcm9tZWRpbyBkZSBsYSB2YXJpYWJsZSBkZXBlbmRpZW50ZT8uCgpSZWNvcmRlbW9zIHF1ZSBjdWFuZG8gY2FsY3VsYW1vcyBsYSBzdW1hIGRlIGxvcyBjdWFkcmFkb3MgZGVsIGVycm9yIHBhcmEgbGEgdmFyaWFibGUgZGVwZW5kaWVudGUgc2Ugb2J0dXZvOiAkU1NFPTEyMCQuIExhIHN1bWEgdG90YWwgZGUgbG9zIGN1YWRyYWRvcyBkZWwgZXJyb3IgKCpTU1QqID0gKlNTRSopIG51bmNhIHNlcsOhIG1heW9yIGEgMTIwIHBhcmEgZXN0ZSBjb25qdW50byBkZSBkYXRvcy4KClRlbmdhbW9zIHByZXNlbnRlIHRhbWJpw6luLCBxdWUgZWwgb2JqZXRpdm8gZXMgcmVkdWNpciBlbCAqU1NFKiBhIHRyYXbDqXMgZGUgbGEgbMOtbmVhIHF1ZSBtZWpvciBzZSBhanVzdGUgYWwgY29qdW50byBkZSBkYXRvcywgeWEgcXVlIGN1YW50byBtZWpvciBzZSBhanVzdGUgZWwgbW9kZWxvLCBtZW5vciBzZXLDoSBlbCB2YWxvciBkZWwgKlNTRSouCgohW10oc3NyLnBuZykKClZhbW9zIGEgb2J0ZW5lciBsYSBzdW1hIGRlIGN1YWRyYWRvcyBkZWwgZXJyb3IgZGViaWRvIGEgbGEgcmVncmVzacOzbiAoKlNTUiopLCBsYSBjdWFsIGVzIGxhIGRpZmVyZW5jaWEgZW50cmUgKlNTVCogeSAqU1NFKi4KCk9idGVuZ2Ftb3MgY29uIGVsIG1vZGVsbywgbG9zIHZhbG9yZXMgcXVlIHByZWRpY2UgcGFyYSBlbCBtb250byBkZSBsYXMgcHJvcGluYXM6CgokJFxoYXR7eV9pfSA9IDAuMTQ2MnhfaS0wLjIxODgkJAoKZG9uZGUsCgokeF9pID0gZmFjdHVyYV9pJCwKCiRcaGF0e3l9ID0gcHJlZGljY2nDs25cIHByb3BpbmEkCgpQb3IgZWplbXBsbyBwYXJhIGVsIHBsYXRvIDMgc2UgdGllbmUgdW5hIGZhY3R1cmEgcG9yIHZhbG9yIGRlICQ2NFVTXCQkLiBQb3IgbG8gdGFudG8gZWwgdmFsb3IgcXVlIHByZWRpY2UgZWwgbW9kZWxvIGVzOgoKJCRcaGF0e3lfaX0gPSAwLjE0NjIoNjQpLTAuMjE4OCA9IDguNTM2NSQkCgpBaG9yYSB2ZWFtb3MgZW4gbGEgdGFibGEgbG9zIHZhbG9yZXMgcHJlZGljaG9zIHBvciBlbCBtb2RlbG86CgpgYGB7cn0KbW9kMSA8LSBsbShwcm9waW5hIH4gZmFjdHVyYSwgZGF0YSA9IHByb3BpbmFzKQoKcHJvcGluYXMgJT4lIAogIG11dGF0ZShwcm9wX3ByZWQgPSBwcmVkaWN0KG1vZDEpKQpgYGAKCiMjIEVycm9yIFByZWRpY2Npb25lcwoKTGEgZGlmZXJlbmNpYSBxdWUgZXhpc3RlIGVudHJlIGVsIHZhbG9yIG9ic2VydmFkbyB5IGVsIHZhbG9yIHByZWRpY2hvLCBlcyBlbCBlcnJvciwgZXMgZGVjaXIgbGEgZGlzdGFuY2lhIHF1ZSBoYXkgZW50cmUgZWwgbGEgcHJlZGljY2nDs24geSBlbCB2YWxvciBvYnNlcnZhZG8uIFRhbWJpw6luIHNlIGxlcyBjb25vY2UgY29tbyByZXNpZHVhbGVzLgoKCmBgYHtyfQpwcm9waW5hcyAlPiUgCiAgbXV0YXRlKHByb3BfcHJlZCA9IHByZWRpY3QobW9kMSkpICU+JQogIGdncGxvdChtYXBwaW5nID0gYWVzKHkgPSBwcm9wX3ByZWQsIHggPSBmYWN0dXJhKSkrCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2UgPSBGKSsKICBnZW9tX3BvaW50KGNvbG9yID0gImRhcmtyZWQiKSsKICBnZW9tX3RleHQoYWVzKGxhYmVsPXJvdW5kKHByb3BfcHJlZCwgZGlnaXRzID0gMikpLGhqdXN0PTAsIHZqdXN0PTEuNSwgY29sb3IgPSAiZGFya3JlZCIpKwogIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyh4ID0gZmFjdHVyYSwgeSA9IHByb3BpbmEpLCBjb2xvciA9ICJibGFjayIpKwogIGxhYnModGl0bGUgPSAiRXJyb3IgTW9kZWxvIGRlIFJlZ3Jlc2nDs24gTGluZWFsIiwKICAgICAgIHggPSAiRmFjdHVyYSIsCiAgICAgICB5ID0gIlByb3BpbmEiKSsKICBzdGF0X3BvbHlfZXEoYWVzKGxhYmVsID0gcGFzdGUoLi5lcS5sYWJlbC4uICwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VwID0gIn5+fn4iKSksCiAgICAgICAgICAgICAgIGZvcm11bGEgPSB5IH4geCwgcGFyc2UgPSBUUlVFLAogICAgICAgICAgICAgICBsYWJlbC54Lm5wYyA9IDAuNSwKICAgICAgICAgICAgICAgY29sb3IgPSAiYmx1ZSIpKwogIHRoZW1lX2J3KCkKYGBgCjxicj4KCiogVGFibGEgZGUgZXJyb3IgeSBjdWFkcmFkbyBkZWwgZXJyb3IKCjxicj4KCmBgYHtyfQpwcm9waW5hcyAlPiUgCiAgbXV0YXRlKHByb3BfcHJlZCA9IHByZWRpY3QobW9kMSksCiAgICAgICAgIGVycm9yID0gcHJvcGluYS1wcm9wX3ByZWQsCiAgICAgICAgIGN1YWRfZXJyb3IgPSAoZXJyb3IpXjIpCmBgYAoKPGJyPgoKIVtdKHNzZWVyci5wbmcpCgo8YnI+CgpMYSBzdW1hIGRlIGxvcyBjdWFkcmFkb3MgZGVsIGVycm9yICpTU0UqIGVzIGByIHN1bSgocHJvcGluYXMkcHJvcGluYSAtIHByZWRpY3QobW9kMSkpXjIpYDoKCmBgYHtyfQpzdW0oKHByb3BpbmFzJHByb3BpbmEgLSBwcmVkaWN0KG1vZDEpKV4yKQpgYGAKCgo8YnI+CgohW10oY3VhZC5wbmcpCgo8YnI+CgohW10oc3VtYS5wbmcpCgoKUG9kZW1vcyBhcHJlY2lhciBlbCBjb21wYXJhdGl2byBkZSBsb3MgdmFsb3JlcyBkZSBTU0UgY3VhbmRvIHPDs2xvIHNlIHRvbWEgY29tbyBwcmVkaWN0b3IgZWwgcHJvbWVkaW8gZGUgbGEgdmFyaWFibGUgeSBjdWFuZG8gYXBsaWNhbW9zIHJlZ3Jlc2nDs24gbGluZWFsIHNpbXBsZTsgZXN0byByZXN1bWUgZWwgb2JqZXRpdm8gZGUgbGEgcmVncmVzacOzbiBsaW5lYWwsIGVsIGN1YWwgZXMgcmVkdWNpciBlbCB2YWxvciBkZSBsYSBzdW1hIGRlIGxvcyBjdWFkcmFkb3MgZGVsIGVycm9yLgoKRWwgbW9kZWxvIHJlZHVqbyBlbCB2YWxvciBkZWwgZXJyb3IsIGRlIDEyMCBhIDMwLjA3NSwgZXMgZGVjaXIgcXVlIHNlIHJlZHVqbyBlbiA4OS45MjUgdW5pZGFkZXMuCgokJFNTVCA9IFNTUiArIFNTRSQkCgokJDEyMCA9IDg5LjkyNSArIDMwLjA3NSQkCkNvbW8gc2UgaGFiw61hIG1lbmNpb25hZG8gYW50ZXJpb3JtZW50ZSwgZWwgdmFsb3IgdG90YWwgbcOheGltbyBxdWUgc2UgcHVlZGUgb2J0ZW5lciBlcyBkZSAxMjAsIHBlcm8gcGFyYSBlbCBjYXNvIGVuIGVsIHF1ZSBzZSBhcGxpY2EgbGEgcmVncmVzacOzbiBsaW5lYWwgc2Ugb2J0aWVuZSB1biB2YWxvciBwYXJhICRTU0U9MzAuMDc1JCB5IHBhcmEgbGEgc3VtYSBkZSBsb3MgY3VhZHJhZG9zIGRlbCBlcnJvciBkZWJpZG8gYSBsYSByZWdyZXNpw7NuLCBlcyBkZSAkU1NSPTg5LjkyNSQuCgoKIyBDb2VmaWNpZW50ZSBkZSBEZXRlcm1pbmFjacOzbgoKwr9RdcOpIHRhbiBiaWVuIHNlIGFqdXN0YSBsYSBsw61uZWEgZGUgcmVncmVzacOzbiBhIGxvcyBkYXRvcz8uCgpFbiBlc3RlIHB1bnRvIGVzIGRvbmRlIGxhIHJlZ3Jlc2nDs24gY29taWVuemEgYSB0ZW5lciBzaW1pbGl0dWQgY29uIEFOT1ZBOyBsYSBzdW1hIHRvdGFsIGRlIGxvcyBjdWFkcmFkb3MgKlNTVCogZXMgZGl2aWRpZGEgZW50cmUgKlNTUiogeSAqU1NFKiB5IGx1ZWdvIHNlIG1pZGUgbGEgcmVsYWNpw7NuIGVudHJlICpTU1IqIHkgKlNTVCouCgpTaSAqU1NSKiBlcyBncmFuZGUsIGxlIGNvcnJlc3BvbmRlIHVuYSBtYXlvciBwYXJ0ZSBkZSAqU1NUKiB5LCBwb3IgbG8gdGFudG8sICpTU0UqIGVzIG3DoXMgcGVxdWXDsW8gZW4gcmVsYWNpw7NuIGNvbiAqU1NUKi4KCkVsIGNvZWZpY2llbnRlIGRlIGRldGVybWluYWNpw7NuICRyXjIkIGVzdMOhIGRhZG8gcG9yOgoKJCRyXjIgPSBcZnJhY3tTU1J9e1NTVH0gPSBcZnJhY3s4OS45MjV9ezEyMH0gPSAwLjc0OTM3NSQkCgojIyBJbnRlcnByZXRhY2nDs24KCkVsIHZhbG9yIHBlcm1pdGUgbWVkaXIgc2kgZWwgbW9kZWxvIGVzLCBvIG5vICwgIGVzdGFkw61zdGljYW1lbnRlIHNpZ2luaWZpY2F0aXZvLgoKRGUgbGEgbWlzbWEgbWFuZXJhIGVsIHZhbG9yIHNlIHB1ZWRlIGRhciBjb21vIHBvcmNlbnRhamUsIGVuIGVzdGUgY2FzbyAkNzQuOTNcJSQuCgpFcyBwb3NpYmxlIGNvbmNsdWlyLCBxdWUgZWwgJDc0LjkzXCUkIGRlIGxhIHN1bWEgdG90YWwgZGUgbG9zIGN1YWRyYWRvcywgcHVlZGUgc2VyIGV4cGxpY2FkbyBhcGxpY2FuZG8gbGEgZWN1YWNpw7NuIGRlIHJlZ3Jlc2nDs24gcGFyYSBwcmVkZWNpciBlbCBtb250byBkZSBsYSBwcm9waW5hLiBFbCByZXN0byAoJDI1LjA3XCUkKSBlcyBkZWJpZG8gYWwgZXJyb3IuCgpEZSBlc3RhIGluZm9ybWFjacOzbiBwb2RlbW9zIGRlY2lyIHF1ZSBlbCBhanVzdGUgcXVlIHNlIG9idGllbmUgZXMgYnVlbm8uCgoKYGBge3J9CnByb3BpbmFzICU+JSAKICBnZ3Bsb3QobWFwcGluZyA9IGFlcyh4ID0gZmFjdHVyYSwgeSA9IHByb3BpbmEpKSsKICBnZW9tX3BvaW50KGNvbG9yID0gIm9yYW5nZSIpKwogIGdlb21fdGV4dChhZXMobGFiZWw9cHJvcGluYSksaGp1c3Q9MCwgdmp1c3Q9MCkrCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2UgPSBGQUxTRSwgY29sb3IgPSAiZm9yZXN0Z3JlZW4iKSsKICBsYWJzKHggPSAiRmFjdHVyYSIsCiAgICAgICB5ID0gIlByb3BpbmEiLAogICAgICAgdGl0bGUgPSAiQ29lZmljaWVudGUgZGUgRGV0ZXJtaW5hY2nDs24iKSsKICAgc3RhdF9wb2x5X2VxKGFlcyhsYWJlbCA9IHBhc3RlKC4uZXEubGFiZWwuLiAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAuLnJyLmxhYmVsLi4gLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZXAgPSAifn5+fiIpKSwKICAgICAgICAgICAgICAgZm9ybXVsYSA9IHkgfiB4LCBwYXJzZSA9IFRSVUUsCiAgICAgICAgICAgICAgIGxhYmVsLngubnBjID0gMC41LAogICAgICAgICAgICAgICBjb2xvciA9ICJzdGVlbGJsdWUiKSsKICB0aGVtZV9idygpCmBgYAoKIyMjIERpZmVyZW5jaWFzIGVudHJlIGxhIHN1bWEgZGUgY3VhZHJhZG9zCgokJFNTRSA9IFxTaWdtYSh5LVxoYXR7eV9pfSleMiQkCgokJFNTVCA9IFxTaWdtYSh5X2kgLSBcYmFye3l9KV4yJCQKCiQkU1NSID0gXFNpZ21hKFxoYXR7eV9pfS0gXGJhcnt5fSleMiQkCgo8YnI+CgohW10oZGlmZXJlLnBuZykKCgojIFJlc3VtZW4gZGVsIE1vZGVsbwoKTGEgZnVuY2nDs24gYGxtKClgIGRlIFIgYXJyb2phIHVuIHJlc3VtZW4gZGVsIG1vZGVsbyBkZSBsYSByZWdyZXNpw7NuIGxpbmVhbDoKCmBgYHtyfQptb2QxIDwtIGxtKHByb3BpbmEgfiBmYWN0dXJhLCBkYXRhID0gcHJvcGluYXMpCnJlc3VtZW5fbW9kZWxvIDwtIHN1bW1hcnkobW9kMSkKcmVzdW1lbl9tb2RlbG8KYGBgCgpFcyBjb252ZW5pZW50ZSB0YW1iacOpbiBhY2NlZGVyIGEgbG9zIGVzdGFkw61zdGljb3MgZGVsIG1vZGVsbyBkZSBtYW5lcmEgaW5kaXZpZHVhbDoKCmBgYHtyfQojIHByZWRpY2Npb25lcyBkZWwgbW9kZWxvCnByZWRpY3QobW9kMSkKYGBgCgoKYGBge3J9CiMgY29lZmljaWVudGVzCmNvZWZmaWNpZW50cyhtb2QxKQpgYGAKCmBgYHtyfQojIHJlc2lkdWFsZXMKcmVzaWR1YWxzKG1vZDEpCmBgYAoKCmBgYHtyfQojIGVycm9yIGVzdGFuZGFyIGRlbCBhanVzdGUKc3VtbWFyeShtb2QxKSRzaWdtYQpgYGAKCgpgYGB7cn0KIyBjdWFkcmFkbyBkZWwgZXJyb3IKc3VtbWFyeShtb2QxKSRyLnNxdWFyZWQgIApgYGAKCkFwbGlxdWVtb3MgQU5PVkEgYSBudWVzdHJvIG1vZGVsbzoKCmBgYHtyfQphbm92YShtb2QxKQpgYGAKCkhhc3RhIGFjw6Egc2UgdGllbmVuIGxhcyBoZXJyYW1pZW50YXMgYsOhc2ljYXMgcGFyYSBhcGxpY2FyIGVsIG1vZGVsbyBkZSByZWdyZXNpw7NuIGxpbmVhbCBzaW1wbGUgeSBhbGd1bmFzIGZ1bmNpb25lcyBkZSBSIHBhcmEgb2J0ZW5lciBhbGd1bm9zIHJlc3VsdGFkb3MgZGVzY3JpcHRpdm9zLg==