1 Introducción

La regresión logística es uno de los métodos más conocidos para predecir el valor de una variable respuesta \(Y\) de tipo categórico, en función de un conjunto de covariables o variables predictoras. Algunos ejemplos del uso de este tipo de regresión son:

  • \(Y=0\) ó \(Y=1\).

  • \(Y=\) no sobrevive o \(Y=\) sobrevive

  • \(Y=\) no paga el crédito o \(Y=\) si paga el crédito

El objetivo de este tipo de modelos es estimar la probabilidad del evento de interés:

\[P(Y=1|X=x)\]

Dado un vector \(X=x\) de covariables o variables predictoras, considerando que \(Y\) tiene solo dos posible valores, se tiene que \(Y \sim Bernoulli(p)\), donde \(P(Y=1)=p\) y \(P(Y=0)=1-p\).

Con un modelo de regresión logística es posible modelar \(P(Y=1|X=x)\). Usando la definción de la función logit se tiene que:

\[P(Y=1|X=x)=logit^-1(\beta^Tx)=\frac{exp(\beta^Tx)}{1+exp(\beta^Tx)}\]

2 Regresión logística aplicada a Titanic

Los datos a analizar corresponden al conjunto de datos del Titanic, los cuales contienen información acerca de los pasajeros que viajaban en la embarcación. Los campos de la base de datos se describen a continuación:

# Retirar columnas innecesarias
titanic <- titanic[,-c(3)]

# Renombrar columnas por facilidad
colnames(titanic) <- c("Sobreviviente", "Clase", "Sexo", "Edad",
                       "Hermanos", "Padres", "Tarifa")

# Codificar las columnas
titanic$Sobreviviente = factor(titanic$Sobreviviente)
titanic$Clase = factor(titanic$Clase)
titanic$Sexo = factor(titanic$Sexo)
  • Sobreviviente: es un variable categórica que representa si el pasajero sobrevive o no.

  • Clase: hace referencia a la clase en la que viajaba el pasajero, posee tres categorías; siendo 1 la clase más alta, es decir los pasajeros más adinerados.

  • Sexo: representa el sexo de los pasajeros, femenino o masculino.

  • Edad: variable numérica que contiene la información de la edad de los pasajeros.

  • Tarifa: es una variable numérica con el precio que cada pasajero pagó para viajar en la embarcación.

  • Hermanos: es una variable numérica discreta que contiene información sobre el número de hermanos o cónyuges a bordo.

  • Padres: es una variable numérica discreta que contiene información sobre el número de padres e hijos a bordo.

2.1 Análisis descriptivo

La primera gráfica a analizar es el comportamiento de sobrevivencia y muerte.

ggplot(data = titanic, aes(Sobreviviente, fill = Sobreviviente)) +
  geom_bar(position="dodge") +
  ggtitle("Sobrevivientes") +
  ylab("Cantidad") + 
  scale_fill_manual(values=c("#87ceeb", "#ffd700")) +
  theme_bw() +
  theme(plot.title = element_text(hjust = 0.5)) +
  theme(legend.position='top')

La base de datos utilizada en este análisis contiene información de \(887\) pasajeros de los cuales \(545\) fallecieron como se observa en la gráfica anterior y los restantes \(342\) lograron sobrevivir. Adicionalmente se concluye que no existe un problema grave de desbalanceo de clases que pueda perjudicar los modelos que se realizan en las secciones posteriores.

Las siguientes gráficas representan la catidad de pasajeros que sobrevivieron y muerion por clase y por sexo.

# Sobrevivientes por Clase
clase <- ggplot(data = titanic, aes(Clase, fill = Sobreviviente)) +
  geom_bar(position="dodge") +
  ggtitle("Sobrevivientes por Clase") +
  ylab("Cantidad") + 
  scale_fill_manual(values=c("#87ceeb", "#ffd700")) +
  theme_bw() +
  theme(plot.title = element_text(hjust = 0.5)) +
  theme(legend.position='top')

# Sobrevivientes por Sexo
sexo <- ggplot(data = titanic, aes(Sexo, fill = Sobreviviente)) + 
  geom_bar(position = "dodge") + 
  ylab("Cantidad") + 
  scale_fill_manual(values=c("#87ceeb", "#ffd700")) +
  ggtitle("Sobrevivientes por Sexo") +
  theme_bw() +
  theme(plot.title = element_text(hjust = 0.5)) +
  theme(axis.text.x = element_text(angle = 90, hjust = 1)) +
  theme(legend.position='top')

plot_grid(clase, sexo)

De la gráfica izquierda es posible identificar con claridad que la clase a la que pertenecia cada pasajero es un factor fundamental para la sobreviviencia o no de las personas, a la clase 3 pertenecian \(487\) pasajeros con bajos recursos económico y por ende presenta la mayor cantidad de personas fallecidas \(368\), mientras que en la clase 1 se ubicaban \(216\) pasajeros de los cuales lograron sobrevivir \(136\) y fallecieron \(80\) personas. Finalmente, para la clase 2 la cantidad de pasajeros fallecidos y sobrevivientes es muy similar, \(97\) y \(87\) respectivamente.

La gráfica de la derecha presenta la sobreviviencia de los pasajeros por sexo, de esta figura también es posible concluir con claridad que el sexo es un factor determinante en la sobrevivencia de las personas a bordo. De las \(314\) mujeres en la embarcación lograron sobrevivir \(233\), mientras que de los \(573\) hombres a bordo únicamente sobreviven \(109\).

# Sobrevivientes por edad
edad <- ggplot(data = titanic, aes(Sobreviviente, Edad)) + 
  geom_boxplot(aes(fill=Sobreviviente), alpha=0.9) + 
  scale_fill_manual(values=c("#87ceeb", "#ffd700")) +
  ggtitle("Sobrevivientes por Edad") +
  theme_bw() +
  theme(plot.title = element_text(hjust = 0.5))+
  theme(legend.position='top')

#Sobrevivientes por Tarifa
tarifa <- ggplot(data = titanic, aes(Sobreviviente, Tarifa)) + 
  geom_boxplot(aes(fill=Sobreviviente), alpha=0.9) + 
  scale_fill_manual(values=c("#87ceeb", "#ffd700")) +
  ggtitle("Sobrevivientes por Tarifa") +
  theme_bw() +
  theme(plot.title = element_text(hjust = 0.5))+
  theme(legend.position='top')
plot_grid(edad, tarifa)

Las gráficas anteriores presentan el comportamiento de la sobrevivencia por las variables numéricas edad y tarifa, incialmente con la variable edad se observan algunos valores atípicos, pero en general la edad no es una varible influyente en la sobreviviencia o no de los pasajeros. Respecto a la tarifa se observa una leve diferencia en la mediana de los sobrevivientes y fallecidos, sin embargo no es un diferencia grande, también posee algunos puntos atípicos, pero no son de gran relevancia.

A continuación se presenta un análisis exploratiorio de las variables categóricas en función de las variables numéricas que se tienen disponibles.

clase_edad <- ggplot(data = titanic, aes(Clase, Edad)) + 
  geom_boxplot(aes(fill=Sobreviviente), alpha=0.9) + 
  scale_fill_manual(values=c("#87ceeb", "#ffd700")) +
  ggtitle("Sobrevivientes por Edad y Clase") +
  theme_bw() +
  theme(plot.title = element_text(hjust = 0.5))+
  theme(legend.position='top')
sexo_edad <- ggplot(data = titanic, aes(Sexo, Edad)) + 
  geom_boxplot(aes(fill=Sobreviviente), alpha=0.9) + 
  scale_fill_manual(values=c("#87ceeb", "#ffd700")) +
  ggtitle("Sobrevivientes por Edad y Sexo") +
  theme_bw() +
  theme(plot.title = element_text(hjust = 0.5))+
  theme(legend.position='top')
plot_grid(clase_edad, sexo_edad)

De la figura izquierda se observa que las personas que viajaban en la clase 1 tenian una edad más avanzada que las personas de la clase 2 y 3, pero dentro de cada clase las edades de sobrevivencia y fallecimiento son muy similares, la variabilidad más alta se presenta en la clase 1. Las edades de los psajeros que sobrevivieron estaban al rededor de los \(36\), \(28\) y \(22\) años para las clases 1, 2 y 3 respectivamente. Adicional a esto se presentan varios valores atípicos en las edades de las personas que no sobrevivieron y que pertenecían a la clase 3.

La figura de la derecha permite evidenciar que tanto mujeres como hombres sobrevivientes tenian edades muy similares, sin embargo, no hay gran diferencia entre las edades de cada sexo ya que los boxplot se traslapan y sus medianas no estan muy alejadas.

Teniendo en cuenta que la clase es un factor determinante para que cada pasajero lograra sobrevivir se realizan gráficos para analizar la sobreviviencia por sexo para cada una de las clases.

ggplot(data = titanic, aes(Sexo, fill = Sobreviviente)) + 
  geom_bar(position = "dodge") +
  facet_wrap(~Clase) + 
  ylab("Cantidad") + 
  scale_fill_manual(values=c("#87ceeb", "#ffd700")) +
  ggtitle("Sobrevivientes por Clase y Sexo") +
  theme_bw() +
  theme(plot.title = element_text(hjust = 0.5)) +
  theme(axis.text.x = element_text(angle = 90, hjust = 1)) +
  theme(legend.position='top')

De la gráfica anterior se observa que en todas las clases la mayor parte de los sobrevivientes eran mujeres, algo de esperarse ya que eran prioridad al momento de abandonar la embarcación, algo también de resaltar es que los hombres fallecidos en la clase 3 son alrededor de 3 veces más de los que muerieron en las demás clases.

2.2 Ajuste del modelo

De manera inicial se realiza una partición para el conjunto de datos, tomando un 80% de las observaciones como datos de entrenamiento y un 20% restante como datos de prueba.Se realiza un proceso de selección de variables y se determina que las variables y no son significativas. El resumen para el modelo completo, ajustado con el conjunto de datos de entrenamiento se presenta a continuación:

#  Partición del conjunto de datos
split <- sample(c(rep(0, 0.8 * nrow(titanic)), rep(1, 0.2 * nrow(titanic))))
train <- titanic[split == 0, ]  
test <- titanic[split == 1, ]  

# Ajuste del modelo completo
fit <- glm(Sobreviviente ~ Clase + Sexo + Edad + Hermanos, data = train,
           family=binomial(logit))
summary(fit)
## 
## Call:
## glm(formula = Sobreviviente ~ Clase + Sexo + Edad + Hermanos, 
##     family = binomial(logit), data = train)
## 
## Deviance Residuals: 
##     Min       1Q   Median       3Q      Max  
## -2.7766  -0.6345  -0.4085   0.6645   2.4152  
## 
## Coefficients:
##              Estimate Std. Error z value Pr(>|z|)    
## (Intercept)  4.361206   0.463203   9.415  < 2e-16 ***
## Clase2      -1.315094   0.296503  -4.435 9.19e-06 ***
## Clase3      -2.414706   0.278987  -8.655  < 2e-16 ***
## Sexomale    -2.615949   0.214723 -12.183  < 2e-16 ***
## Edad        -0.048702   0.008662  -5.622 1.88e-08 ***
## Hermanos    -0.430459   0.120876  -3.561 0.000369 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 954.03  on 709  degrees of freedom
## Residual deviance: 649.55  on 704  degrees of freedom
## AIC: 661.55
## 
## Number of Fisher Scoring iterations: 5

A partir del resumen para el modelo completo es posible que todas las variables utilizadas como predictoras son significativas para predecir la probabilidad de sobrevivencia de los pasajeros.

Además, se presentan algunas de las probabilidades estimadas con el conjunto de datos de entrenamiento:

# Probabilidades estimadas
train$Probabilidades_estimadas <- fit$fitted.values

kable(train[c(1:4,6), c("Sobreviviente", "Clase", "Sexo", "Edad", "Hermanos", "Padres",
             "Probabilidades_estimadas")], digits = 2)
Sobreviviente Clase Sexo Edad Hermanos Padres Probabilidades_estimadas
1 0 3 male 22 1 0 0.10
2 1 1 female 38 1 0 0.89
4 1 1 female 35 1 0 0.90
5 0 3 male 35 0 0 0.09
10 1 2 female 14 1 0 0.87

Por último, es posible observar la matriz de confusión del modelo, junto con las medidas de evaluación para modelos de clasificación:

confusion <- function(mod, data=NULL, cutoff=0.5) {
if (is.null(data)) y <- mod$y
else {
name_y <- deparse(formula(mod)[2])
name_y <- substr(x=name_y, start=1, stop=nchar(name_y)-2)
y <- data[[name_y]]
}
probability <- predict(object=mod, newdata=data, type='response')
prediction <- ifelse(probability <= cutoff, 0, 1)
confusion <- table(Prediction=prediction, Real=y)
TP <- confusion[1, 1]; FP <- confusion[1, 2]
FN <- confusion[2, 1]; TN <- confusion[2, 2]
list(confusion=confusion,
sensitivity=TP / (TP + FN),
specificity=TN /( TN + FP),
accuracy=(TP + TN) / sum(confusion),
false_discovery_rate=FP / (FP + TP))
}
con <- confusion(mod = fit, data = train, cutoff = 0.5)
con
## $confusion
##           Real
## Prediction   0   1
##          0 369  85
##          1  59 197
## 
## $sensitivity
## [1] 0.8621495
## 
## $specificity
## [1] 0.6985816
## 
## $accuracy
## [1] 0.7971831
## 
## $false_discovery_rate
## [1] 0.1872247
  • De acuerdo al accuracy del modelo, se obtiene que el modelo clasifica de manera correcta un 80.7% de las observaciones, aproximadamente.

  • De acuerdo con la especificididad, el modelo predice valores negativos un 70% de las veces.

  • Según la sensibilidad, el modelo tiene una probabilidad del 87.27% de predecir correctamente un individuo clasificado como sobreviviente.

3 Evaluación de supuestos en regresión logística

3.1 Linealidad

Uno de los supuestos más importantes en regresión logística es que la relación entre el \(logit\) o log-odds de la variable respuesta y cada variable predictora o variable independiente es lineal; este supuesto se verifica únicamente para las variables numéricas continuas que se tengan en el modelo.

El \(logit\) es el logaritmo del \(odds \ ratio\), en donde \(p=\) Probabilidad de éxito, que para el conjunto de datos, se refiere a la probabilidad de sobrevivencia del pasajero al hundimiento del barco.

\[logit(p)=log\left(\frac{p}{1-p}\right)\]

3.1.1 Test de Box Tidwell

Para verificar este supuesto, es posible usar el Test Box-Tidwell, el cual se realiza por medio de la función boxTidwell del paquete car. El test se plantea tomando como hipótesis nula el cumplimineto del supuesto de linealidad. Según esto, se puede apreciar el siguiente resultado:

logodds <- fit$linear.predictors
boxTidwell(logodds ~ train$Edad)
##  MLE of lambda Score Statistic (z) Pr(>|z|)
##        0.97816              0.0251     0.98
## 
## iterations =  3

La salida anterior presenta el valor p asociado al estadístico de prueba del test de Box Tidwell, a partir del cual, no se rechaza el supuesto de linealidad entre el \(logit\) y la variable Edad. Además, es posible apreciar el gráfico de la edad vs. el logaritmo de las probabilidades estimadas:

logEdad <- data.frame(logodds, Edad = train$Edad)
ggplot(data = logEdad, aes(x = Edad, y = logodds)) + 
  geom_point() +
  ggtitle("Edad vs Logaritmo del Odds") +
  theme_bw() +
  theme(plot.title = element_text(hjust = 0.5))

El diagrama de dispersión no parece tener un patrón no lineal fuerte, por lo que se puede concluir que no se observa una violación al supuesto de linealidad.

Nota: Para la comprobación de este supuesto en Python, no hay una función integrada de la misma manera que en R, por lo que es posible verificar este supuesto agregando los términos de interacción entre las variables continuas y su correspondiente transformación logarítmica dentro del modelo, o en su defecto, por medio del diagrama de dispersión entre las variables continuas y el logaritmo de las probabilidades estimadas.

3.2 Independencia entre las observaciones

La independencia entre observaciones es uno de los supuestos más importantes. Suele ser el resultado del proceso de recolección de los datos, por lo que usualmente no es fácil detectarlo mediante la evaluación de residuales del modelo. La mejor forma de evaluar este supuesto usualmente es comprender el proceso mediante el cual se recopilaron los datos. Sin embargo, si los datos no fueron recopilados a lo largo del tiempo, este supuesto puede ser verificado por medio del gráfico de los residuos vs. los residuos rezagados un período de tiempo. En un escenario ideal, no se debe observar ningún tipo de patrón aparente.

En el caso de datos espaciales, la independencia puede probarse graficando los residuos vs. variables explicativas espaciales, como latitud o longitud. De igual forma, para la verificación del supuesto de independencia, no se debe observar ningún tipo de patrón aparente en estos gráficos.

Esta suposición de independencia se cumple automáticamente para el conjunto de datos de Titanic. Suposición que sería más preocupante en datos de series de tiempo, donde la autocorrelación puede ser un problema. No obstante, es pñosible verificar la independencia de las observaciones para estos datos gráficamente. Aquí, la ‘variable de tiempo’ es el orden de las observaciones, es decir, los números índice.

En particular, podemos crear la gráfica de los residuales de desviación del modelo logit contra los números índice de las observaciones.

Indice <- seq(1,710,1)
Residuales <- fit$residuals
residuales <- data.frame(Indice, Residuales)
ggplot(data = residuales, aes(x = Indice, y = Residuales)) +
  geom_point() +
  ggtitle("Residuales del modelo ajustado") +
  theme_bw() +
  theme(plot.title = element_text(hjust = 0.5))

El gráfico de dispersión permite observar más residuos positivos que negativos, sin embargo, los puntos se distribuyen de manera homogénea y no se observa ningún patrón aparente que pudiera indicar un grado de dependencia entre las observaciones, por lo que se puede concluir que se cumple el supuesto de independencia entre observaciones para el conjunto de datos de Titanic, en particular.

3.3 Ausencia de multicolinealidad

Se dice que hay presencia de multicolinealidad cuando dos o más variables dentro del conjunto de datos mantienen una relación lineal. Existe una colinealidad perfecta cuando alguna covariable puede ser representada como combinación lineal de una o más covariables presentes en los datos. Cuando esto sucede, debe eliminarse aquella covariable que genera un grado de dependencia con las demás.

Usualmente es posible trabajar con un grado de correlación moderado, ya que cuando la correlación entre variables es muy alta, se genera un incremento en los errores estándar, lo cual hace que los coeficientes estimados no sean confiables, y en consecuencia, las estimaciones sean poco creíbles.

La metodología a seguir para probar este supuesto consiste en evaluar inicialmente los coeficientes de correlación entre las variables explicativas. Luego, es necesario verificar colinealidad por medio de los valores de tolerancia o los factores infladores de varianza (VIF). Finalmente, si a partir de estas medidas hay sospecha de multicolinealidad, esto puede ser confirmado por medio del índice de condición, los valores propios y las proporciones de varianza.

3.3.1 Correlación

Inicialmente se presenta un mapa de calor con la correlación existente entre las dos variables numéricas que fueron consideradas en el ajuste del modelo.

cormat <- round(cor(titanic[,c(4,5)]),2)
library(reshape2)
melted_cormat <- melt(cormat)
library(ggplot2)
ggplot(data = melted_cormat, aes(x=Var1, y=Var2, fill=value)) + 
  geom_tile()

El anterior mapa de calor permite concluir que existe una correlación negativa entre las variables Edad y Hermanos para los datos que se estan analizando, dicha correlación esta alrededor de \(-0.25\).

Para verificar este valor de correlación se realiza el siguiente gráfico, donde se presenta el histograma para cada variable, valor de correlación y gráfico de dispersión.

correlaciones <- cor(titanic[,c("Edad", "Tarifa")])

my_data <- titanic[, c(4,5)]
chart.Correlation(my_data, histogram=TRUE, pch=19)

Se observa que la variable edad no posee una distribución simétrica, ya que es sesgada a la derecha. Además,la variable hermanos es una variable numérica discreta. El gráfico de dispersión no muestra patrones claros de correlación, sin embargo, la correlación de estas dos variables es de \(-0.3\), lo cual no es preocupante en términos de violación del supuesto.

3.3.2 Factores infladores de varianza o VIF

Los factores infladores de varianza o VIF determinan el grado de relación entre variables independientes. Además, vienen determinados por el ajuste de una regresión entre ambas variables. La tolerancia está definida como el recíproco de los valores VIF, por lo que se puede verificar el supuesto con cualquiera de estas dos medidas.

A continuación se presentan los valores para los estadísticos VIF para las variables numéricas que se tienen en el modelo.

kable(vif(fit), digits = 2)
GVIF Df GVIF^(1/(2*Df))
Clase 1.46 2 1.10
Sexo 1.13 1 1.06
Edad 1.52 1 1.23
Hermanos 1.20 1 1.10

Sería problemático obtener valores VIF mayores a 5 o 10, ya que esto generaría un sesgo en el modelo, por lo que sería viable descartar alguna de las variables predictoras. Para el conjunto de datos considerado, los valores en la tabla parecen indicar que no hay un problema evidente de multicolinealidad.

4 Conclusiones

El conjunto de datos seleccionado contiene información acerca de las personas que viajaban en el barco al momento de la tragedia, después de ajustado el modelo y validado los tres supuestos principales para este tipo de modelos de clasificación se determina que el conjunto de datos cumple con todos los supuestos, por lo tanto, es un modelo adecuado para predecir si un pasajero del Titanic sobrevive o no al hundimiento del barco.

5 Referencias

LS0tDQp0aXRsZTogIkV2YWx1YWNpw7NuIGRlIHN1cHVlc3RvcyBlbiByZWdyZXNpw7NuIGxvZ8Otc3RpY2EiDQphdXRob3I6ICJDYW1pbGEgQWNvc3RhIFJhbcOtcmV6IC0gU3RlcGhhbnkgTWljaGVsbCBMb2JvIExhZ3VhZG8gPGJyPiBVbml2ZXJzaWRhZCBOYWNpb25hbCBkZSBDb2xvbWJpYSA8YnI+IFNlZGUgTWVkZWxsw61uIg0KZGF0ZTogXHRvZGF5DQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQogICAgY29kZV9kb3dubG9hZDogeWVzDQogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMNCiAgICB0aGVtZTogc3BhY2VsYWINCiAgICB0b2M6IHllcw0KICAgIHRvY19mbG9hdDoNCiAgICAgIGNvbGxhcHNlZDogeWVzDQotLS0NCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpDQpgYGANCg0KIyBJbnRyb2R1Y2Npw7NuDQoNCkxhIHJlZ3Jlc2nDs24gbG9nw61zdGljYSBlcyB1bm8gZGUgbG9zIG3DqXRvZG9zIG3DoXMgY29ub2NpZG9zIHBhcmEgcHJlZGVjaXIgZWwgdmFsb3IgZGUgdW5hIHZhcmlhYmxlIHJlc3B1ZXN0YSAkWSQgZGUgdGlwbyBjYXRlZ8OzcmljbywgZW4gZnVuY2nDs24gZGUgdW4gY29uanVudG8gZGUgY292YXJpYWJsZXMgbyB2YXJpYWJsZXMgcHJlZGljdG9yYXMuIEFsZ3Vub3MgZWplbXBsb3MgZGVsIHVzbyBkZSBlc3RlIHRpcG8gZGUgcmVncmVzacOzbiBzb246IA0KDQoqICRZPTAkIMOzICRZPTEkLg0KDQoqICRZPSQgbm8gc29icmV2aXZlIG8gJFk9JCBzb2JyZXZpdmUgDQoNCiogJFk9JCBubyBwYWdhIGVsIGNyw6lkaXRvIG8gJFk9JCBzaSBwYWdhIGVsIGNyw6lkaXRvDQoNCkVsIG9iamV0aXZvIGRlIGVzdGUgdGlwbyBkZSBtb2RlbG9zIGVzIGVzdGltYXIgbGEgcHJvYmFiaWxpZGFkIGRlbCBldmVudG8gZGUgaW50ZXLDqXM6DQoNCiQkUChZPTF8WD14KSQkDQoNCkRhZG8gdW4gdmVjdG9yICRYPXgkIGRlIGNvdmFyaWFibGVzIG8gdmFyaWFibGVzIHByZWRpY3RvcmFzLCBjb25zaWRlcmFuZG8gcXVlICRZJCB0aWVuZSBzb2xvIGRvcyBwb3NpYmxlIHZhbG9yZXMsIHNlIHRpZW5lIHF1ZSAkWSBcc2ltIEJlcm5vdWxsaShwKSQsIGRvbmRlICRQKFk9MSk9cCQgeSAkUChZPTApPTEtcCQuDQoNCkNvbiB1biBtb2RlbG8gZGUgcmVncmVzacOzbiBsb2fDrXN0aWNhIGVzIHBvc2libGUgbW9kZWxhciAkUChZPTF8WD14KSQuIFVzYW5kbyBsYSBkZWZpbmNpw7NuIGRlIGxhIGZ1bmNpw7NuIGBsb2dpdGAgc2UgdGllbmUgcXVlOg0KDQokJFAoWT0xfFg9eCk9bG9naXReLTEoXGJldGFeVHgpPVxmcmFje2V4cChcYmV0YV5UeCl9ezErZXhwKFxiZXRhXlR4KX0kJA0KDQojIFJlZ3Jlc2nDs24gbG9nw61zdGljYSBhcGxpY2FkYSBhIFRpdGFuaWMNCg0KYGBge3IgaW5jbHVkZT1GQUxTRX0NCmxpYnJhcnkoY293cGxvdCkNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoY29ycnBsb3QpDQpsaWJyYXJ5KGNhcikNCmxpYnJhcnkoa25pdHIpDQpsaWJyYXJ5KFBlcmZvcm1hbmNlQW5hbHl0aWNzKQ0KYGBgDQoNCmBgYHtyIGluY2x1ZGU9RkFMU0V9DQp0aXRhbmljIDwtIHJlYWQuY3N2KCJDOi9Vc2Vycy9TdGVwaGFueS9EZXNrdG9wLzIwMjItMS9HTE0vdGl0YW5pYy5jc3YiKQ0KY29sbmFtZXModGl0YW5pYykNCmBgYA0KDQpMb3MgZGF0b3MgYSBhbmFsaXphciBjb3JyZXNwb25kZW4gYWwgY29uanVudG8gZGUgZGF0b3MgZGVsIFtUaXRhbmljXShodHRwOi8vd2ViLnN0YW5mb3JkLmVkdS9jbGFzcy9hcmNoaXZlL2NzL2NzMTA5L2NzMTA5LjExNjYvcHJvYmxlbTEyLmh0bWwpLCBsb3MgY3VhbGVzIGNvbnRpZW5lbiBpbmZvcm1hY2nDs24gYWNlcmNhIGRlIGxvcyBwYXNhamVyb3MgcXVlIHZpYWphYmFuIGVuIGxhIGVtYmFyY2FjacOzbi4gTG9zIGNhbXBvcyAgZGUgbGEgYmFzZSBkZSBkYXRvcyBzZSBkZXNjcmliZW4gYSBjb250aW51YWNpw7NuOg0KDQpgYGB7cn0NCiMgUmV0aXJhciBjb2x1bW5hcyBpbm5lY2VzYXJpYXMNCnRpdGFuaWMgPC0gdGl0YW5pY1ssLWMoMyldDQoNCiMgUmVub21icmFyIGNvbHVtbmFzIHBvciBmYWNpbGlkYWQNCmNvbG5hbWVzKHRpdGFuaWMpIDwtIGMoIlNvYnJldml2aWVudGUiLCAiQ2xhc2UiLCAiU2V4byIsICJFZGFkIiwNCiAgICAgICAgICAgICAgICAgICAgICAgIkhlcm1hbm9zIiwgIlBhZHJlcyIsICJUYXJpZmEiKQ0KDQojIENvZGlmaWNhciBsYXMgY29sdW1uYXMNCnRpdGFuaWMkU29icmV2aXZpZW50ZSA9IGZhY3Rvcih0aXRhbmljJFNvYnJldml2aWVudGUpDQp0aXRhbmljJENsYXNlID0gZmFjdG9yKHRpdGFuaWMkQ2xhc2UpDQp0aXRhbmljJFNleG8gPSBmYWN0b3IodGl0YW5pYyRTZXhvKQ0KYGBgDQoNCiogU29icmV2aXZpZW50ZTogZXMgdW4gdmFyaWFibGUgY2F0ZWfDs3JpY2EgcXVlIHJlcHJlc2VudGEgc2kgZWwgcGFzYWplcm8gc29icmV2aXZlIG8gbm8uDQoNCiogQ2xhc2U6IGhhY2UgcmVmZXJlbmNpYSBhIGxhIGNsYXNlIGVuIGxhIHF1ZSB2aWFqYWJhIGVsIHBhc2FqZXJvLCBwb3NlZSB0cmVzIGNhdGVnb3LDrWFzOyBzaWVuZG8gMSBsYSBjbGFzZSBtw6FzIGFsdGEsIGVzIGRlY2lyIGxvcyBwYXNhamVyb3MgbcOhcyBhZGluZXJhZG9zLg0KDQoqIFNleG86IHJlcHJlc2VudGEgZWwgc2V4byBkZSBsb3MgcGFzYWplcm9zLCBmZW1lbmlubyBvIG1hc2N1bGluby4NCg0KKiBFZGFkOiB2YXJpYWJsZSBudW3DqXJpY2EgcXVlIGNvbnRpZW5lIGxhIGluZm9ybWFjacOzbiBkZSBsYSBlZGFkIGRlIGxvcyBwYXNhamVyb3MuDQoNCiogVGFyaWZhOiBlcyB1bmEgdmFyaWFibGUgbnVtw6lyaWNhIGNvbiBlbCBwcmVjaW8gcXVlIGNhZGEgcGFzYWplcm8gcGFnw7MgcGFyYSB2aWFqYXIgZW4gbGEgZW1iYXJjYWNpw7NuLg0KDQoqIEhlcm1hbm9zOiBlcyB1bmEgdmFyaWFibGUgbnVtw6lyaWNhIGRpc2NyZXRhIHF1ZSBjb250aWVuZSBpbmZvcm1hY2nDs24gc29icmUgZWwgbsO6bWVybyBkZSBoZXJtYW5vcyBvIGPDs255dWdlcyBhIGJvcmRvLg0KDQoqIFBhZHJlczogZXMgdW5hIHZhcmlhYmxlIG51bcOpcmljYSBkaXNjcmV0YSBxdWUgY29udGllbmUgaW5mb3JtYWNpw7NuIHNvYnJlIGVsIG7Dum1lcm8gZGUgcGFkcmVzIGUgaGlqb3MgYSBib3Jkby4NCg0KIyMgQW7DoWxpc2lzIGRlc2NyaXB0aXZvDQoNCkxhIHByaW1lcmEgZ3LDoWZpY2EgYSBhbmFsaXphciBlcyBlbCBjb21wb3J0YW1pZW50byBkZSBzb2JyZXZpdmVuY2lhIHkgbXVlcnRlLg0KDQpgYGB7cn0NCmdncGxvdChkYXRhID0gdGl0YW5pYywgYWVzKFNvYnJldml2aWVudGUsIGZpbGwgPSBTb2JyZXZpdmllbnRlKSkgKw0KICBnZW9tX2Jhcihwb3NpdGlvbj0iZG9kZ2UiKSArDQogIGdndGl0bGUoIlNvYnJldml2aWVudGVzIikgKw0KICB5bGFiKCJDYW50aWRhZCIpICsgDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKCIjODdjZWViIiwgIiNmZmQ3MDAiKSkgKw0KICB0aGVtZV9idygpICsNCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSd0b3AnKQ0KYGBgDQoNCkxhIGJhc2UgZGUgZGF0b3MgdXRpbGl6YWRhIGVuIGVzdGUgYW7DoWxpc2lzIGNvbnRpZW5lIGluZm9ybWFjacOzbiBkZSAkODg3JCBwYXNhamVyb3MgZGUgbG9zIGN1YWxlcyAkNTQ1JCBmYWxsZWNpZXJvbiBjb21vIHNlIG9ic2VydmEgZW4gbGEgZ3LDoWZpY2EgYW50ZXJpb3IgeSBsb3MgcmVzdGFudGVzICQzNDIkIGxvZ3Jhcm9uIHNvYnJldml2aXIuIEFkaWNpb25hbG1lbnRlIHNlIGNvbmNsdXllIHF1ZSBubyBleGlzdGUgdW4gcHJvYmxlbWEgZ3JhdmUgZGUgZGVzYmFsYW5jZW8gZGUgY2xhc2VzIHF1ZSBwdWVkYSBwZXJqdWRpY2FyIGxvcyBtb2RlbG9zIHF1ZSBzZSByZWFsaXphbiBlbiBsYXMgc2VjY2lvbmVzIHBvc3RlcmlvcmVzLg0KDQpMYXMgc2lndWllbnRlcyBncsOhZmljYXMgcmVwcmVzZW50YW4gbGEgY2F0aWRhZCBkZSBwYXNhamVyb3MgcXVlIHNvYnJldml2aWVyb24geSBtdWVyaW9uIHBvciBjbGFzZSB5IHBvciBzZXhvLg0KDQpgYGB7ciBmaWcuYWxpZ249J2NlbnRlcicsIG91dC53aWR0aD0iMTAwJSJ9DQojIFNvYnJldml2aWVudGVzIHBvciBDbGFzZQ0KY2xhc2UgPC0gZ2dwbG90KGRhdGEgPSB0aXRhbmljLCBhZXMoQ2xhc2UsIGZpbGwgPSBTb2JyZXZpdmllbnRlKSkgKw0KICBnZW9tX2Jhcihwb3NpdGlvbj0iZG9kZ2UiKSArDQogIGdndGl0bGUoIlNvYnJldml2aWVudGVzIHBvciBDbGFzZSIpICsNCiAgeWxhYigiQ2FudGlkYWQiKSArIA0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9YygiIzg3Y2VlYiIsICIjZmZkNzAwIikpICsNCiAgdGhlbWVfYncoKSArDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ndG9wJykNCg0KIyBTb2JyZXZpdmllbnRlcyBwb3IgU2V4bw0Kc2V4byA8LSBnZ3Bsb3QoZGF0YSA9IHRpdGFuaWMsIGFlcyhTZXhvLCBmaWxsID0gU29icmV2aXZpZW50ZSkpICsgDQogIGdlb21fYmFyKHBvc2l0aW9uID0gImRvZGdlIikgKyANCiAgeWxhYigiQ2FudGlkYWQiKSArIA0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9YygiIzg3Y2VlYiIsICIjZmZkNzAwIikpICsNCiAgZ2d0aXRsZSgiU29icmV2aXZpZW50ZXMgcG9yIFNleG8iKSArDQogIHRoZW1lX2J3KCkgKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ndG9wJykNCg0KcGxvdF9ncmlkKGNsYXNlLCBzZXhvKQ0KYGBgDQoNCkRlIGxhIGdyw6FmaWNhIGl6cXVpZXJkYSBlcyBwb3NpYmxlIGlkZW50aWZpY2FyIGNvbiBjbGFyaWRhZCBxdWUgbGEgY2xhc2UgYSBsYSBxdWUgcGVydGVuZWNpYSBjYWRhIHBhc2FqZXJvIGVzIHVuIGZhY3RvciBmdW5kYW1lbnRhbCBwYXJhIGxhIHNvYnJldml2aWVuY2lhIG8gbm8gZGUgbGFzIHBlcnNvbmFzLCBhIGxhIGNsYXNlIDMgcGVydGVuZWNpYW4gJDQ4NyQgcGFzYWplcm9zIGNvbiBiYWpvcyByZWN1cnNvcyBlY29uw7NtaWNvIHkgcG9yIGVuZGUgcHJlc2VudGEgbGEgbWF5b3IgY2FudGlkYWQgZGUgcGVyc29uYXMgZmFsbGVjaWRhcyAkMzY4JCwgbWllbnRyYXMgcXVlIGVuIGxhIGNsYXNlIDEgc2UgdWJpY2FiYW4gJDIxNiQgcGFzYWplcm9zIGRlIGxvcyBjdWFsZXMgbG9ncmFyb24gc29icmV2aXZpciAkMTM2JCB5IGZhbGxlY2llcm9uICQ4MCQgcGVyc29uYXMuIEZpbmFsbWVudGUsIHBhcmEgbGEgY2xhc2UgMiBsYSBjYW50aWRhZCBkZSBwYXNhamVyb3MgZmFsbGVjaWRvcyB5IHNvYnJldml2aWVudGVzIGVzIG11eSBzaW1pbGFyLCAkOTckIHkgJDg3JCByZXNwZWN0aXZhbWVudGUuDQoNCkxhIGdyw6FmaWNhIGRlIGxhIGRlcmVjaGEgcHJlc2VudGEgbGEgc29icmV2aXZpZW5jaWEgZGUgbG9zIHBhc2FqZXJvcyBwb3Igc2V4bywgZGUgZXN0YSBmaWd1cmEgdGFtYmnDqW4gZXMgcG9zaWJsZSBjb25jbHVpciBjb24gY2xhcmlkYWQgcXVlIGVsIHNleG8gZXMgdW4gZmFjdG9yIGRldGVybWluYW50ZSBlbiBsYSBzb2JyZXZpdmVuY2lhIGRlIGxhcyBwZXJzb25hcyBhIGJvcmRvLiBEZSBsYXMgJDMxNCQgbXVqZXJlcyBlbiBsYSBlbWJhcmNhY2nDs24gbG9ncmFyb24gc29icmV2aXZpciAkMjMzJCwgbWllbnRyYXMgcXVlIGRlIGxvcyAkNTczJCBob21icmVzIGEgYm9yZG8gw7puaWNhbWVudGUgc29icmV2aXZlbiAkMTA5JC4NCg0KYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCBvdXQud2lkdGg9IjEwMCUifQ0KIyBTb2JyZXZpdmllbnRlcyBwb3IgZWRhZA0KZWRhZCA8LSBnZ3Bsb3QoZGF0YSA9IHRpdGFuaWMsIGFlcyhTb2JyZXZpdmllbnRlLCBFZGFkKSkgKyANCiAgZ2VvbV9ib3hwbG90KGFlcyhmaWxsPVNvYnJldml2aWVudGUpLCBhbHBoYT0wLjkpICsgDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKCIjODdjZWViIiwgIiNmZmQ3MDAiKSkgKw0KICBnZ3RpdGxlKCJTb2JyZXZpdmllbnRlcyBwb3IgRWRhZCIpICsNCiAgdGhlbWVfYncoKSArDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKSsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSd0b3AnKQ0KDQojU29icmV2aXZpZW50ZXMgcG9yIFRhcmlmYQ0KdGFyaWZhIDwtIGdncGxvdChkYXRhID0gdGl0YW5pYywgYWVzKFNvYnJldml2aWVudGUsIFRhcmlmYSkpICsgDQogIGdlb21fYm94cGxvdChhZXMoZmlsbD1Tb2JyZXZpdmllbnRlKSwgYWxwaGE9MC45KSArIA0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9YygiIzg3Y2VlYiIsICIjZmZkNzAwIikpICsNCiAgZ2d0aXRsZSgiU29icmV2aXZpZW50ZXMgcG9yIFRhcmlmYSIpICsNCiAgdGhlbWVfYncoKSArDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKSsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSd0b3AnKQ0KcGxvdF9ncmlkKGVkYWQsIHRhcmlmYSkNCmBgYA0KDQpMYXMgZ3LDoWZpY2FzIGFudGVyaW9yZXMgcHJlc2VudGFuIGVsIGNvbXBvcnRhbWllbnRvIGRlIGxhIHNvYnJldml2ZW5jaWEgcG9yIGxhcyB2YXJpYWJsZXMgbnVtw6lyaWNhcyBlZGFkIHkgdGFyaWZhLCBpbmNpYWxtZW50ZSBjb24gbGEgdmFyaWFibGUgZWRhZCBzZSBvYnNlcnZhbiBhbGd1bm9zIHZhbG9yZXMgYXTDrXBpY29zLCBwZXJvIGVuIGdlbmVyYWwgbGEgZWRhZCBubyBlcyB1bmEgdmFyaWJsZSBpbmZsdXllbnRlIGVuIGxhIHNvYnJldml2aWVuY2lhIG8gbm8gZGUgbG9zIHBhc2FqZXJvcy4gUmVzcGVjdG8gYSBsYSB0YXJpZmEgc2Ugb2JzZXJ2YSB1bmEgbGV2ZSBkaWZlcmVuY2lhIGVuIGxhIG1lZGlhbmEgZGUgbG9zIHNvYnJldml2aWVudGVzIHkgZmFsbGVjaWRvcywgc2luIGVtYmFyZ28gbm8gZXMgdW4gZGlmZXJlbmNpYSBncmFuZGUsIHRhbWJpw6luIHBvc2VlIGFsZ3Vub3MgcHVudG9zIGF0w61waWNvcywgcGVybyBubyBzb24gZGUgZ3JhbiByZWxldmFuY2lhLg0KDQpBIGNvbnRpbnVhY2nDs24gc2UgcHJlc2VudGEgdW4gYW7DoWxpc2lzIGV4cGxvcmF0aW9yaW8gZGUgbGFzIHZhcmlhYmxlcyBjYXRlZ8OzcmljYXMgZW4gZnVuY2nDs24gZGUgbGFzIHZhcmlhYmxlcyBudW3DqXJpY2FzIHF1ZSBzZSB0aWVuZW4gZGlzcG9uaWJsZXMuDQoNCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJywgb3V0LndpZHRoPSIxMDAlIn0NCmNsYXNlX2VkYWQgPC0gZ2dwbG90KGRhdGEgPSB0aXRhbmljLCBhZXMoQ2xhc2UsIEVkYWQpKSArIA0KICBnZW9tX2JveHBsb3QoYWVzKGZpbGw9U29icmV2aXZpZW50ZSksIGFscGhhPTAuOSkgKyANCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoIiM4N2NlZWIiLCAiI2ZmZDcwMCIpKSArDQogIGdndGl0bGUoIlNvYnJldml2aWVudGVzIHBvciBFZGFkIHkgQ2xhc2UiKSArDQogIHRoZW1lX2J3KCkgKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkrDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ndG9wJykNCnNleG9fZWRhZCA8LSBnZ3Bsb3QoZGF0YSA9IHRpdGFuaWMsIGFlcyhTZXhvLCBFZGFkKSkgKyANCiAgZ2VvbV9ib3hwbG90KGFlcyhmaWxsPVNvYnJldml2aWVudGUpLCBhbHBoYT0wLjkpICsgDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKCIjODdjZWViIiwgIiNmZmQ3MDAiKSkgKw0KICBnZ3RpdGxlKCJTb2JyZXZpdmllbnRlcyBwb3IgRWRhZCB5IFNleG8iKSArDQogIHRoZW1lX2J3KCkgKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkrDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ndG9wJykNCnBsb3RfZ3JpZChjbGFzZV9lZGFkLCBzZXhvX2VkYWQpDQpgYGANCg0KRGUgbGEgZmlndXJhIGl6cXVpZXJkYSBzZSBvYnNlcnZhIHF1ZSBsYXMgcGVyc29uYXMgcXVlIHZpYWphYmFuIGVuIGxhIGNsYXNlIDEgdGVuaWFuIHVuYSBlZGFkIG3DoXMgYXZhbnphZGEgcXVlIGxhcyBwZXJzb25hcyBkZSBsYSBjbGFzZSAyIHkgMywgcGVybyBkZW50cm8gZGUgY2FkYSBjbGFzZSBsYXMgZWRhZGVzIGRlIHNvYnJldml2ZW5jaWEgeSBmYWxsZWNpbWllbnRvIHNvbiBtdXkgc2ltaWxhcmVzLCBsYSB2YXJpYWJpbGlkYWQgbcOhcyBhbHRhIHNlIHByZXNlbnRhIGVuIGxhIGNsYXNlIDEuIExhcyBlZGFkZXMgZGUgbG9zIHBzYWplcm9zIHF1ZSBzb2JyZXZpdmllcm9uIGVzdGFiYW4gYWwgcmVkZWRvciBkZSBsb3MgJDM2JCwgJDI4JCB5ICQyMiQgYcOxb3MgcGFyYSBsYXMgY2xhc2VzIDEsIDIgeSAzIHJlc3BlY3RpdmFtZW50ZS4gQWRpY2lvbmFsIGEgZXN0byBzZSBwcmVzZW50YW4gdmFyaW9zIHZhbG9yZXMgYXTDrXBpY29zIGVuIGxhcyBlZGFkZXMgZGUgbGFzIHBlcnNvbmFzIHF1ZSBubyBzb2JyZXZpdmllcm9uIHkgcXVlIHBlcnRlbmVjw61hbiBhIGxhIGNsYXNlIDMuDQoNCkxhIGZpZ3VyYSBkZSBsYSBkZXJlY2hhIHBlcm1pdGUgZXZpZGVuY2lhciBxdWUgdGFudG8gbXVqZXJlcyBjb21vIGhvbWJyZXMgc29icmV2aXZpZW50ZXMgdGVuaWFuIGVkYWRlcyBtdXkgc2ltaWxhcmVzLCBzaW4gZW1iYXJnbywgbm8gaGF5IGdyYW4gZGlmZXJlbmNpYSBlbnRyZSBsYXMgZWRhZGVzIGRlIGNhZGEgc2V4byB5YSBxdWUgbG9zIGJveHBsb3Qgc2UgdHJhc2xhcGFuIHkgc3VzIG1lZGlhbmFzIG5vIGVzdGFuIG11eSBhbGVqYWRhcy4NCg0KVGVuaWVuZG8gZW4gY3VlbnRhIHF1ZSBsYSBjbGFzZSBlcyB1biBmYWN0b3IgZGV0ZXJtaW5hbnRlIHBhcmEgcXVlIGNhZGEgcGFzYWplcm8gbG9ncmFyYSBzb2JyZXZpdmlyIHNlIHJlYWxpemFuIGdyw6FmaWNvcyBwYXJhIGFuYWxpemFyIGxhIHNvYnJldml2aWVuY2lhIHBvciBzZXhvIHBhcmEgY2FkYSB1bmEgZGUgbGFzIGNsYXNlcy4NCg0KYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCBvdXQud2lkdGg9IjEwMCUifQ0KZ2dwbG90KGRhdGEgPSB0aXRhbmljLCBhZXMoU2V4bywgZmlsbCA9IFNvYnJldml2aWVudGUpKSArIA0KICBnZW9tX2Jhcihwb3NpdGlvbiA9ICJkb2RnZSIpICsNCiAgZmFjZXRfd3JhcCh+Q2xhc2UpICsgDQogIHlsYWIoIkNhbnRpZGFkIikgKyANCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoIiM4N2NlZWIiLCAiI2ZmZDcwMCIpKSArDQogIGdndGl0bGUoIlNvYnJldml2aWVudGVzIHBvciBDbGFzZSB5IFNleG8iKSArDQogIHRoZW1lX2J3KCkgKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ndG9wJykNCmBgYA0KDQpEZSBsYSBncsOhZmljYSBhbnRlcmlvciBzZSBvYnNlcnZhIHF1ZSBlbiB0b2RhcyBsYXMgY2xhc2VzIGxhIG1heW9yIHBhcnRlIGRlIGxvcyBzb2JyZXZpdmllbnRlcyBlcmFuIG11amVyZXMsIGFsZ28gZGUgZXNwZXJhcnNlIHlhIHF1ZSBlcmFuIHByaW9yaWRhZCBhbCBtb21lbnRvIGRlIGFiYW5kb25hciBsYSBlbWJhcmNhY2nDs24sIGFsZ28gdGFtYmnDqW4gZGUgcmVzYWx0YXIgZXMgcXVlIGxvcyBob21icmVzIGZhbGxlY2lkb3MgZW4gbGEgY2xhc2UgMyBzb24gYWxyZWRlZG9yIGRlIDMgdmVjZXMgbcOhcyBkZSBsb3MgcXVlIG11ZXJpZXJvbiBlbiBsYXMgZGVtw6FzIGNsYXNlcy4NCg0KDQojIyBBanVzdGUgZGVsIG1vZGVsbw0KDQpEZSBtYW5lcmEgaW5pY2lhbCBzZSByZWFsaXphIHVuYSBwYXJ0aWNpw7NuIHBhcmEgZWwgY29uanVudG8gZGUgZGF0b3MsIHRvbWFuZG8gdW4gODAlIGRlIGxhcyBvYnNlcnZhY2lvbmVzIGNvbW8gZGF0b3MgZGUgZW50cmVuYW1pZW50byB5IHVuIDIwJSByZXN0YW50ZSBjb21vIGRhdG9zIGRlIHBydWViYS5TZSByZWFsaXphIHVuIHByb2Nlc28gZGUgc2VsZWNjacOzbiBkZSB2YXJpYWJsZXMgeSBzZSBkZXRlcm1pbmEgcXVlIGxhcyB2YXJpYWJsZXMgXHRleHRpdHtQYWRyZXN9IHkgXHRleHRpdHtUYXJpZmF9IG5vIHNvbiBzaWduaWZpY2F0aXZhcy4gRWwgcmVzdW1lbiBwYXJhIGVsIG1vZGVsbyBjb21wbGV0bywgYWp1c3RhZG8gY29uIGVsIGNvbmp1bnRvIGRlIGRhdG9zIGRlIGVudHJlbmFtaWVudG8gc2UgcHJlc2VudGEgYSBjb250aW51YWNpw7NuOiANCg0KDQpgYGB7ciBlY2hvPVRSVUV9DQojICBQYXJ0aWNpw7NuIGRlbCBjb25qdW50byBkZSBkYXRvcw0Kc3BsaXQgPC0gc2FtcGxlKGMocmVwKDAsIDAuOCAqIG5yb3codGl0YW5pYykpLCByZXAoMSwgMC4yICogbnJvdyh0aXRhbmljKSkpKQ0KdHJhaW4gPC0gdGl0YW5pY1tzcGxpdCA9PSAwLCBdICANCnRlc3QgPC0gdGl0YW5pY1tzcGxpdCA9PSAxLCBdICANCg0KIyBBanVzdGUgZGVsIG1vZGVsbyBjb21wbGV0bw0KZml0IDwtIGdsbShTb2JyZXZpdmllbnRlIH4gQ2xhc2UgKyBTZXhvICsgRWRhZCArIEhlcm1hbm9zLCBkYXRhID0gdHJhaW4sDQogICAgICAgICAgIGZhbWlseT1iaW5vbWlhbChsb2dpdCkpDQpzdW1tYXJ5KGZpdCkNCmBgYA0KDQpBIHBhcnRpciBkZWwgcmVzdW1lbiBwYXJhIGVsIG1vZGVsbyBjb21wbGV0byBlcyBwb3NpYmxlIHF1ZSB0b2RhcyBsYXMgdmFyaWFibGVzIHV0aWxpemFkYXMgY29tbyBwcmVkaWN0b3JhcyBzb24gc2lnbmlmaWNhdGl2YXMgcGFyYSBwcmVkZWNpciBsYSBwcm9iYWJpbGlkYWQgZGUgc29icmV2aXZlbmNpYSBkZSBsb3MgcGFzYWplcm9zLg0KDQpBZGVtw6FzLCBzZSBwcmVzZW50YW4gYWxndW5hcyBkZSBsYXMgcHJvYmFiaWxpZGFkZXMgZXN0aW1hZGFzIGNvbiBlbCBjb25qdW50byBkZSBkYXRvcyBkZSBlbnRyZW5hbWllbnRvOiANCg0KYGBge3IgZWNobz1UUlVFfQ0KIyBQcm9iYWJpbGlkYWRlcyBlc3RpbWFkYXMNCnRyYWluJFByb2JhYmlsaWRhZGVzX2VzdGltYWRhcyA8LSBmaXQkZml0dGVkLnZhbHVlcw0KDQprYWJsZSh0cmFpbltjKDE6NCw2KSwgYygiU29icmV2aXZpZW50ZSIsICJDbGFzZSIsICJTZXhvIiwgIkVkYWQiLCAiSGVybWFub3MiLCAiUGFkcmVzIiwNCiAgICAgICAgICAgICAiUHJvYmFiaWxpZGFkZXNfZXN0aW1hZGFzIildLCBkaWdpdHMgPSAyKQ0KYGBgDQoNClBvciDDumx0aW1vLCBlcyBwb3NpYmxlIG9ic2VydmFyICBsYSBtYXRyaXogZGUgY29uZnVzacOzbiBkZWwgbW9kZWxvLCBqdW50byBjb24gbGFzIG1lZGlkYXMgZGUgZXZhbHVhY2nDs24gcGFyYSBtb2RlbG9zIGRlIGNsYXNpZmljYWNpw7NuOiANCg0KYGBge3IgZWNobz1UUlVFfQ0KY29uZnVzaW9uIDwtIGZ1bmN0aW9uKG1vZCwgZGF0YT1OVUxMLCBjdXRvZmY9MC41KSB7DQppZiAoaXMubnVsbChkYXRhKSkgeSA8LSBtb2QkeQ0KZWxzZSB7DQpuYW1lX3kgPC0gZGVwYXJzZShmb3JtdWxhKG1vZClbMl0pDQpuYW1lX3kgPC0gc3Vic3RyKHg9bmFtZV95LCBzdGFydD0xLCBzdG9wPW5jaGFyKG5hbWVfeSktMikNCnkgPC0gZGF0YVtbbmFtZV95XV0NCn0NCnByb2JhYmlsaXR5IDwtIHByZWRpY3Qob2JqZWN0PW1vZCwgbmV3ZGF0YT1kYXRhLCB0eXBlPSdyZXNwb25zZScpDQpwcmVkaWN0aW9uIDwtIGlmZWxzZShwcm9iYWJpbGl0eSA8PSBjdXRvZmYsIDAsIDEpDQpjb25mdXNpb24gPC0gdGFibGUoUHJlZGljdGlvbj1wcmVkaWN0aW9uLCBSZWFsPXkpDQpUUCA8LSBjb25mdXNpb25bMSwgMV07IEZQIDwtIGNvbmZ1c2lvblsxLCAyXQ0KRk4gPC0gY29uZnVzaW9uWzIsIDFdOyBUTiA8LSBjb25mdXNpb25bMiwgMl0NCmxpc3QoY29uZnVzaW9uPWNvbmZ1c2lvbiwNCnNlbnNpdGl2aXR5PVRQIC8gKFRQICsgRk4pLA0Kc3BlY2lmaWNpdHk9VE4gLyggVE4gKyBGUCksDQphY2N1cmFjeT0oVFAgKyBUTikgLyBzdW0oY29uZnVzaW9uKSwNCmZhbHNlX2Rpc2NvdmVyeV9yYXRlPUZQIC8gKEZQICsgVFApKQ0KfQ0KY29uIDwtIGNvbmZ1c2lvbihtb2QgPSBmaXQsIGRhdGEgPSB0cmFpbiwgY3V0b2ZmID0gMC41KQ0KY29uDQpgYGANCg0KKiBEZSBhY3VlcmRvIGFsIGFjY3VyYWN5IGRlbCBtb2RlbG8sIHNlIG9idGllbmUgcXVlIGVsIG1vZGVsbyBjbGFzaWZpY2EgZGUgbWFuZXJhIGNvcnJlY3RhIHVuIDgwLjclIGRlIGxhcyBvYnNlcnZhY2lvbmVzLCBhcHJveGltYWRhbWVudGUuDQoNCiogRGUgYWN1ZXJkbyBjb24gbGEgZXNwZWNpZmljaWRpZGFkLCBlbCBtb2RlbG8gcHJlZGljZSB2YWxvcmVzIG5lZ2F0aXZvcyB1biA3MCUgZGUgbGFzIHZlY2VzLg0KDQoqIFNlZ8O6biBsYSBzZW5zaWJpbGlkYWQsIGVsIG1vZGVsbyB0aWVuZSB1bmEgcHJvYmFiaWxpZGFkIGRlbCA4Ny4yNyUgZGUgcHJlZGVjaXIgY29ycmVjdGFtZW50ZSB1biBpbmRpdmlkdW8gY2xhc2lmaWNhZG8gY29tbyBzb2JyZXZpdmllbnRlLg0KDQojIEV2YWx1YWNpw7NuIGRlIHN1cHVlc3RvcyBlbiByZWdyZXNpw7NuIGxvZ8Otc3RpY2ENCg0KIyMgTGluZWFsaWRhZA0KDQpVbm8gZGUgbG9zIHN1cHVlc3RvcyBtw6FzIGltcG9ydGFudGVzIGVuIHJlZ3Jlc2nDs24gbG9nw61zdGljYSBlcyBxdWUgbGEgcmVsYWNpw7NuIGVudHJlIGVsICRsb2dpdCQgbyBsb2ctb2RkcyBkZSBsYSB2YXJpYWJsZSByZXNwdWVzdGEgeSBjYWRhIHZhcmlhYmxlIHByZWRpY3RvcmEgbyB2YXJpYWJsZSBpbmRlcGVuZGllbnRlIGVzIGxpbmVhbDsgZXN0ZSBzdXB1ZXN0byBzZSB2ZXJpZmljYSDDum5pY2FtZW50ZSBwYXJhIGxhcyB2YXJpYWJsZXMgbnVtw6lyaWNhcyBjb250aW51YXMgcXVlIHNlIHRlbmdhbiBlbiBlbCBtb2RlbG8uDQoNCkVsICRsb2dpdCQgZXMgZWwgbG9nYXJpdG1vIGRlbCAkb2RkcyBcIHJhdGlvJCwgZW4gZG9uZGUgJHA9JCBQcm9iYWJpbGlkYWQgZGUgw6l4aXRvLCBxdWUgcGFyYSBlbCBjb25qdW50byBkZSBkYXRvcywgc2UgcmVmaWVyZSBhIGxhIHByb2JhYmlsaWRhZCBkZSBzb2JyZXZpdmVuY2lhIGRlbCBwYXNhamVybyBhbCBodW5kaW1pZW50byBkZWwgYmFyY28uDQoNCiQkbG9naXQocCk9bG9nXGxlZnQoXGZyYWN7cH17MS1wfVxyaWdodCkkJA0KDQojIyMgVGVzdCBkZSBCb3ggVGlkd2VsbA0KDQpQYXJhIHZlcmlmaWNhciBlc3RlIHN1cHVlc3RvLCBlcyBwb3NpYmxlIHVzYXIgZWwgVGVzdCBCb3gtVGlkd2VsbCwgZWwgY3VhbCBzZSByZWFsaXphIHBvciBtZWRpbyBkZSBsYSBmdW5jacOzbiBgYm94VGlkd2VsbGAgZGVsIHBhcXVldGUgYGNhcmAuIEVsIHRlc3Qgc2UgcGxhbnRlYSB0b21hbmRvIGNvbW8gaGlww7N0ZXNpcyBudWxhIGVsIGN1bXBsaW1pbmV0byBkZWwgc3VwdWVzdG8gZGUgbGluZWFsaWRhZC4gU2Vnw7puIGVzdG8sIHNlIHB1ZWRlIGFwcmVjaWFyIGVsIHNpZ3VpZW50ZSByZXN1bHRhZG86DQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpsb2dvZGRzIDwtIGZpdCRsaW5lYXIucHJlZGljdG9ycw0KYm94VGlkd2VsbChsb2dvZGRzIH4gdHJhaW4kRWRhZCkNCmBgYA0KDQpMYSBzYWxpZGEgYW50ZXJpb3IgcHJlc2VudGEgZWwgdmFsb3IgcCBhc29jaWFkbyBhbCBlc3RhZMOtc3RpY28gZGUgcHJ1ZWJhIGRlbCB0ZXN0IGRlIEJveCBUaWR3ZWxsLCBhIHBhcnRpciBkZWwgY3VhbCwgbm8gc2UgcmVjaGF6YSBlbCBzdXB1ZXN0byBkZSBsaW5lYWxpZGFkIGVudHJlIGVsICRsb2dpdCQgeSBsYSB2YXJpYWJsZSBFZGFkLiBBZGVtw6FzLCBlcyBwb3NpYmxlIGFwcmVjaWFyIGVsIGdyw6FmaWNvIGRlIGxhIGVkYWQgdnMuIGVsIGxvZ2FyaXRtbyBkZSBsYXMgcHJvYmFiaWxpZGFkZXMgZXN0aW1hZGFzOg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBmaWcuYWxpZ249J2NlbnRlcicsIG91dC53aWR0aD0iMTAwJSJ9DQpsb2dFZGFkIDwtIGRhdGEuZnJhbWUobG9nb2RkcywgRWRhZCA9IHRyYWluJEVkYWQpDQpnZ3Bsb3QoZGF0YSA9IGxvZ0VkYWQsIGFlcyh4ID0gRWRhZCwgeSA9IGxvZ29kZHMpKSArIA0KICBnZW9tX3BvaW50KCkgKw0KICBnZ3RpdGxlKCJFZGFkIHZzIExvZ2FyaXRtbyBkZWwgT2RkcyIpICsNCiAgdGhlbWVfYncoKSArDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQ0KYGBgDQoNCkVsIGRpYWdyYW1hIGRlIGRpc3BlcnNpw7NuIG5vIHBhcmVjZSB0ZW5lciB1biBwYXRyw7NuIG5vIGxpbmVhbCBmdWVydGUsIHBvciBsbyBxdWUgc2UgcHVlZGUgY29uY2x1aXIgcXVlIG5vIHNlIG9ic2VydmEgdW5hIHZpb2xhY2nDs24gYWwgc3VwdWVzdG8gZGUgbGluZWFsaWRhZC4NCg0KKipOb3RhOioqIFBhcmEgbGEgY29tcHJvYmFjacOzbiBkZSBlc3RlIHN1cHVlc3RvIGVuIFB5dGhvbiwgbm8gaGF5IHVuYSBmdW5jacOzbiBpbnRlZ3JhZGEgZGUgbGEgbWlzbWEgbWFuZXJhIHF1ZSBlbiBSLCBwb3IgbG8gcXVlIGVzIHBvc2libGUgdmVyaWZpY2FyIGVzdGUgc3VwdWVzdG8gYWdyZWdhbmRvIGxvcyB0w6lybWlub3MgZGUgaW50ZXJhY2Npw7NuIGVudHJlIGxhcyB2YXJpYWJsZXMgY29udGludWFzIHkgc3UgY29ycmVzcG9uZGllbnRlIHRyYW5zZm9ybWFjacOzbiBsb2dhcsOtdG1pY2EgZGVudHJvIGRlbCBtb2RlbG8sIG8gZW4gc3UgZGVmZWN0bywgcG9yIG1lZGlvIGRlbCBkaWFncmFtYSBkZSBkaXNwZXJzacOzbiBlbnRyZSBsYXMgdmFyaWFibGVzIGNvbnRpbnVhcyB5IGVsIGxvZ2FyaXRtbyBkZSBsYXMgcHJvYmFiaWxpZGFkZXMgZXN0aW1hZGFzLg0KDQojIyBJbmRlcGVuZGVuY2lhIGVudHJlIGxhcyBvYnNlcnZhY2lvbmVzDQoNCkxhIGluZGVwZW5kZW5jaWEgZW50cmUgb2JzZXJ2YWNpb25lcyBlcyB1bm8gZGUgbG9zIHN1cHVlc3RvcyBtw6FzIGltcG9ydGFudGVzLiBTdWVsZSBzZXIgZWwgcmVzdWx0YWRvIGRlbCBwcm9jZXNvIGRlIHJlY29sZWNjacOzbiBkZSBsb3MgZGF0b3MsIHBvciBsbyBxdWUgdXN1YWxtZW50ZSBubyBlcyBmw6FjaWwgZGV0ZWN0YXJsbyBtZWRpYW50ZSBsYSBldmFsdWFjacOzbiBkZSByZXNpZHVhbGVzIGRlbCBtb2RlbG8uIExhIG1lam9yIGZvcm1hIGRlIGV2YWx1YXIgZXN0ZSBzdXB1ZXN0byB1c3VhbG1lbnRlIGVzIGNvbXByZW5kZXIgZWwgcHJvY2VzbyBtZWRpYW50ZSBlbCBjdWFsIHNlIHJlY29waWxhcm9uIGxvcyBkYXRvcy4gU2luIGVtYmFyZ28sIHNpIGxvcyBkYXRvcyBubyBmdWVyb24gcmVjb3BpbGFkb3MgYSBsbyBsYXJnbyBkZWwgdGllbXBvLCBlc3RlIHN1cHVlc3RvIHB1ZWRlIHNlciB2ZXJpZmljYWRvIHBvciBtZWRpbyBkZWwgZ3LDoWZpY28gZGUgbG9zIHJlc2lkdW9zIHZzLiBsb3MgcmVzaWR1b3MgcmV6YWdhZG9zIHVuIHBlcsOtb2RvIGRlIHRpZW1wby4gRW4gdW4gZXNjZW5hcmlvIGlkZWFsLCBubyBzZSBkZWJlIG9ic2VydmFyIG5pbmfDum4gdGlwbyBkZSBwYXRyw7NuIGFwYXJlbnRlLg0KDQpFbiBlbCBjYXNvIGRlIGRhdG9zIGVzcGFjaWFsZXMsIGxhIGluZGVwZW5kZW5jaWEgcHVlZGUgcHJvYmFyc2UgZ3JhZmljYW5kbyBsb3MgcmVzaWR1b3MgdnMuIHZhcmlhYmxlcyBleHBsaWNhdGl2YXMgZXNwYWNpYWxlcywgY29tbyBsYXRpdHVkIG8gbG9uZ2l0dWQuIERlIGlndWFsIGZvcm1hLCBwYXJhIGxhIHZlcmlmaWNhY2nDs24gZGVsIHN1cHVlc3RvIGRlIGluZGVwZW5kZW5jaWEsIG5vIHNlIGRlYmUgb2JzZXJ2YXIgbmluZ8O6biB0aXBvIGRlIHBhdHLDs24gYXBhcmVudGUgZW4gZXN0b3MgZ3LDoWZpY29zLg0KDQpFc3RhIHN1cG9zaWNpw7NuIGRlIGluZGVwZW5kZW5jaWEgc2UgY3VtcGxlIGF1dG9tw6F0aWNhbWVudGUgcGFyYSBlbCBjb25qdW50byBkZSBkYXRvcyBkZSBUaXRhbmljLiBTdXBvc2ljacOzbiBxdWUgc2Vyw61hIG3DoXMgcHJlb2N1cGFudGUgZW4gZGF0b3MgZGUgc2VyaWVzIGRlIHRpZW1wbywgZG9uZGUgbGEgYXV0b2NvcnJlbGFjacOzbiBwdWVkZSBzZXIgdW4gcHJvYmxlbWEuIE5vIG9ic3RhbnRlLCBlcyBww7Fvc2libGUgdmVyaWZpY2FyIGxhIGluZGVwZW5kZW5jaWEgZGUgbGFzIG9ic2VydmFjaW9uZXMgcGFyYSBlc3RvcyBkYXRvcyBncsOhZmljYW1lbnRlLiBBcXXDrSwgbGEgJ3ZhcmlhYmxlIGRlIHRpZW1wbycgZXMgZWwgb3JkZW4gZGUgbGFzIG9ic2VydmFjaW9uZXMsIGVzIGRlY2lyLCBsb3MgbsO6bWVyb3Mgw61uZGljZS4NCg0KRW4gcGFydGljdWxhciwgcG9kZW1vcyBjcmVhciBsYSBncsOhZmljYSBkZSBsb3MgcmVzaWR1YWxlcyBkZSBkZXN2aWFjacOzbiBkZWwgbW9kZWxvIGxvZ2l0IGNvbnRyYSBsb3MgbsO6bWVyb3Mgw61uZGljZSBkZSBsYXMgb2JzZXJ2YWNpb25lcy4NCg0KYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCBvdXQud2lkdGg9IjEwMCUifQ0KSW5kaWNlIDwtIHNlcSgxLDcxMCwxKQ0KUmVzaWR1YWxlcyA8LSBmaXQkcmVzaWR1YWxzDQpyZXNpZHVhbGVzIDwtIGRhdGEuZnJhbWUoSW5kaWNlLCBSZXNpZHVhbGVzKQ0KZ2dwbG90KGRhdGEgPSByZXNpZHVhbGVzLCBhZXMoeCA9IEluZGljZSwgeSA9IFJlc2lkdWFsZXMpKSArDQogIGdlb21fcG9pbnQoKSArDQogIGdndGl0bGUoIlJlc2lkdWFsZXMgZGVsIG1vZGVsbyBhanVzdGFkbyIpICsNCiAgdGhlbWVfYncoKSArDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQ0KYGBgDQoNCkVsIGdyw6FmaWNvIGRlIGRpc3BlcnNpw7NuIHBlcm1pdGUgb2JzZXJ2YXIgbcOhcyByZXNpZHVvcyBwb3NpdGl2b3MgcXVlIG5lZ2F0aXZvcywgc2luIGVtYmFyZ28sIGxvcyBwdW50b3Mgc2UgZGlzdHJpYnV5ZW4gZGUgbWFuZXJhIGhvbW9nw6luZWEgeSBubyBzZSBvYnNlcnZhIG5pbmfDum4gcGF0csOzbiBhcGFyZW50ZSBxdWUgcHVkaWVyYSBpbmRpY2FyIHVuIGdyYWRvIGRlIGRlcGVuZGVuY2lhIGVudHJlIGxhcyBvYnNlcnZhY2lvbmVzLCBwb3IgbG8gcXVlIHNlIHB1ZWRlIGNvbmNsdWlyIHF1ZSBzZSBjdW1wbGUgZWwgc3VwdWVzdG8gZGUgaW5kZXBlbmRlbmNpYSBlbnRyZSBvYnNlcnZhY2lvbmVzIHBhcmEgZWwgY29uanVudG8gZGUgZGF0b3MgZGUgVGl0YW5pYywgZW4gcGFydGljdWxhci4NCg0KIyMgQXVzZW5jaWEgZGUgbXVsdGljb2xpbmVhbGlkYWQNCg0KU2UgZGljZSBxdWUgaGF5IHByZXNlbmNpYSBkZSBtdWx0aWNvbGluZWFsaWRhZCBjdWFuZG8gZG9zIG8gbcOhcyB2YXJpYWJsZXMgZGVudHJvIGRlbCBjb25qdW50byBkZSBkYXRvcyBtYW50aWVuZW4gdW5hIHJlbGFjacOzbiBsaW5lYWwuIEV4aXN0ZSB1bmEgY29saW5lYWxpZGFkIHBlcmZlY3RhIGN1YW5kbyBhbGd1bmEgY292YXJpYWJsZSAgcHVlZGUgc2VyIHJlcHJlc2VudGFkYSBjb21vIGNvbWJpbmFjacOzbiBsaW5lYWwgZGUgdW5hIG8gbcOhcyBjb3ZhcmlhYmxlcyBwcmVzZW50ZXMgZW4gbG9zIGRhdG9zLiBDdWFuZG8gZXN0byBzdWNlZGUsIGRlYmUgZWxpbWluYXJzZSBhcXVlbGxhIGNvdmFyaWFibGUgcXVlIGdlbmVyYSB1biBncmFkbyBkZSBkZXBlbmRlbmNpYSBjb24gbGFzIGRlbcOhcy4NCg0KVXN1YWxtZW50ZSBlcyBwb3NpYmxlIHRyYWJhamFyIGNvbiB1biBncmFkbyBkZSBjb3JyZWxhY2nDs24gbW9kZXJhZG8sIHlhIHF1ZSBjdWFuZG8gbGEgY29ycmVsYWNpw7NuIGVudHJlIHZhcmlhYmxlcyBlcyBtdXkgYWx0YSwgc2UgZ2VuZXJhIHVuIGluY3JlbWVudG8gZW4gbG9zIGVycm9yZXMgZXN0w6FuZGFyLCBsbyBjdWFsIGhhY2UgcXVlIGxvcyBjb2VmaWNpZW50ZXMgZXN0aW1hZG9zIG5vIHNlYW4gY29uZmlhYmxlcywgeSBlbiBjb25zZWN1ZW5jaWEsIGxhcyBlc3RpbWFjaW9uZXMgc2VhbiBwb2NvIGNyZcOtYmxlcy4NCg0KTGEgbWV0b2RvbG9nw61hIGEgc2VndWlyIHBhcmEgcHJvYmFyIGVzdGUgc3VwdWVzdG8gY29uc2lzdGUgZW4gZXZhbHVhciBpbmljaWFsbWVudGUgbG9zIGNvZWZpY2llbnRlcyBkZSBjb3JyZWxhY2nDs24gZW50cmUgbGFzIHZhcmlhYmxlcyBleHBsaWNhdGl2YXMuIEx1ZWdvLCBlcyBuZWNlc2FyaW8gdmVyaWZpY2FyIGNvbGluZWFsaWRhZCBwb3IgbWVkaW8gZGUgbG9zIHZhbG9yZXMgZGUgdG9sZXJhbmNpYSBvIGxvcyBmYWN0b3JlcyBpbmZsYWRvcmVzIGRlIHZhcmlhbnphIChWSUYpLiBGaW5hbG1lbnRlLCBzaSBhIHBhcnRpciBkZSBlc3RhcyBtZWRpZGFzIGhheSBzb3NwZWNoYSBkZSBtdWx0aWNvbGluZWFsaWRhZCwgZXN0byBwdWVkZSBzZXIgY29uZmlybWFkbyBwb3IgbWVkaW8gZGVsIMOtbmRpY2UgZGUgY29uZGljacOzbiwgbG9zIHZhbG9yZXMgcHJvcGlvcyB5IGxhcyBwcm9wb3JjaW9uZXMgZGUgdmFyaWFuemEuDQoNCiMjIyBDb3JyZWxhY2nDs24NCg0KSW5pY2lhbG1lbnRlIHNlIHByZXNlbnRhIHVuIG1hcGEgZGUgY2Fsb3IgY29uIGxhIGNvcnJlbGFjacOzbiBleGlzdGVudGUgZW50cmUgbGFzIGRvcyB2YXJpYWJsZXMgbnVtw6lyaWNhcyBxdWUgZnVlcm9uIGNvbnNpZGVyYWRhcyBlbiBlbCBhanVzdGUgZGVsIG1vZGVsby4NCg0KYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCBvdXQud2lkdGg9IjgwJSJ9DQpjb3JtYXQgPC0gcm91bmQoY29yKHRpdGFuaWNbLGMoNCw1KV0pLDIpDQpsaWJyYXJ5KHJlc2hhcGUyKQ0KbWVsdGVkX2Nvcm1hdCA8LSBtZWx0KGNvcm1hdCkNCmxpYnJhcnkoZ2dwbG90MikNCmdncGxvdChkYXRhID0gbWVsdGVkX2Nvcm1hdCwgYWVzKHg9VmFyMSwgeT1WYXIyLCBmaWxsPXZhbHVlKSkgKyANCiAgZ2VvbV90aWxlKCkNCmBgYA0KDQpFbCBhbnRlcmlvciBtYXBhIGRlIGNhbG9yIHBlcm1pdGUgY29uY2x1aXIgcXVlIGV4aXN0ZSB1bmEgY29ycmVsYWNpw7NuIG5lZ2F0aXZhIGVudHJlIGxhcyB2YXJpYWJsZXMgRWRhZCB5IEhlcm1hbm9zIHBhcmEgbG9zIGRhdG9zIHF1ZSBzZSBlc3RhbiBhbmFsaXphbmRvLCBkaWNoYSBjb3JyZWxhY2nDs24gZXN0YSBhbHJlZGVkb3IgZGUgJC0wLjI1JC4NCg0KUGFyYSB2ZXJpZmljYXIgZXN0ZSB2YWxvciBkZSBjb3JyZWxhY2nDs24gc2UgcmVhbGl6YSBlbCBzaWd1aWVudGUgZ3LDoWZpY28sIGRvbmRlIHNlIHByZXNlbnRhIGVsIGhpc3RvZ3JhbWEgcGFyYSBjYWRhIHZhcmlhYmxlLCB2YWxvciBkZSBjb3JyZWxhY2nDs24geSBncsOhZmljbyBkZSBkaXNwZXJzacOzbi4NCg0KYGBge3IgZWNobz1UUlVFLCBmaWcuYWxpZ249J2NlbnRlcicsIHdhcm5pbmc9RkFMU0UsIG91dC53aWR0aD0nNzAlJ30NCmNvcnJlbGFjaW9uZXMgPC0gY29yKHRpdGFuaWNbLGMoIkVkYWQiLCAiVGFyaWZhIildKQ0KDQpteV9kYXRhIDwtIHRpdGFuaWNbLCBjKDQsNSldDQpjaGFydC5Db3JyZWxhdGlvbihteV9kYXRhLCBoaXN0b2dyYW09VFJVRSwgcGNoPTE5KQ0KYGBgDQoNClNlIG9ic2VydmEgcXVlIGxhIHZhcmlhYmxlIGVkYWQgbm8gcG9zZWUgdW5hIGRpc3RyaWJ1Y2nDs24gc2ltw6l0cmljYSwgeWEgcXVlIGVzIHNlc2dhZGEgYSBsYSBkZXJlY2hhLiBBZGVtw6FzLGxhIHZhcmlhYmxlIGhlcm1hbm9zIGVzIHVuYSB2YXJpYWJsZSBudW3DqXJpY2EgZGlzY3JldGEuIEVsIGdyw6FmaWNvIGRlIGRpc3BlcnNpw7NuIG5vIG11ZXN0cmEgcGF0cm9uZXMgY2xhcm9zIGRlIGNvcnJlbGFjacOzbiwgc2luIGVtYmFyZ28sIGxhIGNvcnJlbGFjacOzbiBkZSBlc3RhcyBkb3MgdmFyaWFibGVzIGVzIGRlICQtMC4zJCwgbG8gY3VhbCBubyBlcyBwcmVvY3VwYW50ZSBlbiB0w6lybWlub3MgZGUgdmlvbGFjacOzbiBkZWwgc3VwdWVzdG8uDQoNCiMjIyBGYWN0b3JlcyBpbmZsYWRvcmVzIGRlIHZhcmlhbnphIG8gVklGDQoNCkxvcyBmYWN0b3JlcyBpbmZsYWRvcmVzIGRlIHZhcmlhbnphIG8gVklGIGRldGVybWluYW4gIGVsIGdyYWRvIGRlIHJlbGFjacOzbiBlbnRyZSB2YXJpYWJsZXMgaW5kZXBlbmRpZW50ZXMuIEFkZW3DoXMsIHZpZW5lbiBkZXRlcm1pbmFkb3MgcG9yIGVsIGFqdXN0ZSBkZSB1bmEgcmVncmVzacOzbiBlbnRyZSBhbWJhcyB2YXJpYWJsZXMuIExhIHRvbGVyYW5jaWEgZXN0w6EgZGVmaW5pZGEgY29tbyBlbCByZWPDrXByb2NvIGRlIGxvcyB2YWxvcmVzIFZJRiwgcG9yIGxvIHF1ZSBzZSBwdWVkZSB2ZXJpZmljYXIgZWwgc3VwdWVzdG8gY29uIGN1YWxxdWllcmEgZGUgZXN0YXMgZG9zIG1lZGlkYXMuDQoNCkEgY29udGludWFjacOzbiBzZSBwcmVzZW50YW4gbG9zIHZhbG9yZXMgcGFyYSBsb3MgZXN0YWTDrXN0aWNvcyBWSUYgcGFyYSBsYXMgdmFyaWFibGVzIG51bcOpcmljYXMgcXVlIHNlIHRpZW5lbiBlbiBlbCBtb2RlbG8uDQoNCmBgYHtyfQ0Ka2FibGUodmlmKGZpdCksIGRpZ2l0cyA9IDIpDQpgYGANCg0KU2Vyw61hIHByb2JsZW3DoXRpY28gb2J0ZW5lciB2YWxvcmVzIFZJRiBtYXlvcmVzIGEgNSBvIDEwLCAgeWEgcXVlIGVzdG8gZ2VuZXJhcsOtYSB1biBzZXNnbyBlbiBlbCBtb2RlbG8sIHBvciBsbyBxdWUgc2Vyw61hIHZpYWJsZSBkZXNjYXJ0YXIgYWxndW5hIGRlIGxhcyB2YXJpYWJsZXMgcHJlZGljdG9yYXMuIFBhcmEgZWwgY29uanVudG8gZGUgZGF0b3MgY29uc2lkZXJhZG8sIGxvcyB2YWxvcmVzIGVuIGxhIHRhYmxhIHBhcmVjZW4gaW5kaWNhciBxdWUgbm8gaGF5IHVuIHByb2JsZW1hIGV2aWRlbnRlIGRlIG11bHRpY29saW5lYWxpZGFkLg0KDQojIENvbmNsdXNpb25lcw0KDQpFbCBjb25qdW50byBkZSBkYXRvcyBzZWxlY2Npb25hZG8gY29udGllbmUgaW5mb3JtYWNpw7NuIGFjZXJjYSBkZSBsYXMgcGVyc29uYXMgcXVlIHZpYWphYmFuIGVuIGVsIGJhcmNvIGFsIG1vbWVudG8gZGUgbGEgdHJhZ2VkaWEsIGRlc3B1w6lzIGRlIGFqdXN0YWRvIGVsIG1vZGVsbyB5IHZhbGlkYWRvIGxvcyB0cmVzIHN1cHVlc3RvcyBwcmluY2lwYWxlcyBwYXJhIGVzdGUgdGlwbyBkZSBtb2RlbG9zIGRlIGNsYXNpZmljYWNpw7NuIHNlIGRldGVybWluYSBxdWUgZWwgY29uanVudG8gZGUgZGF0b3MgY3VtcGxlIGNvbiB0b2RvcyBsb3Mgc3VwdWVzdG9zLCBwb3IgbG8gdGFudG8sIGVzIHVuIG1vZGVsbyBhZGVjdWFkbyBwYXJhIHByZWRlY2lyIHNpIHVuIHBhc2FqZXJvIGRlbCBUaXRhbmljIHNvYnJldml2ZSBvIG5vIGFsIGh1bmRpbWllbnRvIGRlbCBiYXJjby4NCg0KIyBSZWZlcmVuY2lhcw0KDQoqIEJveCwgRy4gRS4gUC4gYW5kIFRpZHdlbGwsIFAuIFcuICgxOTYyKSBUcmFuc2Zvcm1hdGlvbiBvZiB0aGUgaW5kZXBlbmRlbnQgdmFyaWFibGVzLiBUZWNobm9tZXRyaWNzIDQsIDUzMS01NTAuIEZveCwgSi4gKDE5OTcpIEFwcGxpZWQgUmVncmVzc2lvbiwgTGluZWFyIE1vZGVscywgYW5kIFJlbGF0ZWQgTWV0aG9kcy4gU2FnZS4NCg0KKiBEdW5uICYgU215dGgsIEdlbmVyYWxpemVkIExpbmVhciBNb2RlbHMgV2l0aCBFeGFtcGxlcyBpbiBSLCBTcHJpbmdlciAoMjAxOCkuDQoNCiogSGVybmFuZGV6LCBGcmVkZHksIGFuZCBNYXpvIE1hdXJpY2lvLiAyMDIwLiBNb2RlbG9zIGRlIHJlZ3Jlc2nDs24gY29uIFIuIGh0dHBzOi8vZmhlcm5hbmIuZ2l0aHViLmlvL2xpYnJvX3JlZ3Jlc2lvbi8uDQoNCiogSmHDqW4sIE0uIEUuIEYuIChuLmQuKS4gRmVpciA0NTogUmVncmVzacOzbiBMb2fDrXN0aWNhLiBSZXRyaWV2ZWQgTWF5IDIyLCAyMDIyLCBmcm9tIGh0dHBzOi8vZ2F1c3MuaW5mLnVtLmVzL2ZlaXIvNDUvIzI2X3N1cHVlc3Rvc19kZWxfbW9kZWxvLg0KDQoqIFNlbmF2aXJhdG5hLCBOLiBBLiBNLiBSLiwgJiBDb29yYXksIFQuIE0uIEouIEEuICgyMDE5KS4gRGlhZ25vc2luZyBtdWx0aWNvbGxpbmVhcml0eSBvZiBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsLiBBc2lhbiBKb3VybmFsIG9mIFByb2JhYmlsaXR5IGFuZCBTdGF0aXN0aWNzLCA1KDIpLCAxLTkuDQoNCiogVGhpbGFrc2hhc2lsdmEuICgyMDE3LCBEZWNlbWJlciAxNCkuIFByZWRpY3RpbmcgdGl0YW5pYyBzdXJ2aXZhbCB1c2luZyBmaXZlIGFsZ29yaXRobXMuIEthZ2dsZS4gUmV0cmlldmVkIE1heSAyMiwgMjAyMiwgZnJvbSBodHRwczovL3d3dy5rYWdnbGUuY29tL2NvZGUvdGhpbGFrc2hhc2lsdmEvcHJlZGljdGluZy10aXRhbmljLXN1cnZpdmFsLXVzaW5nLWZpdmUtYWxnb3JpdGhtcyNpbnRyb2R1Y3Rpb24uDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQo=