Fuente de información

  • Fuente: CRAN Datasets - Urine Analysis Data.
  • Descripción: registros con información de 79 muestras de orina con características físicas.
  • Variables: están registradas las siguientes variables:
    • r: indicador de presencia de oxalato de calcio. (Variable Objetivo)
      • 0: ausencia de oxalato en orina.
      • 1: presencia de oxalato en orina.
    • gravity: gravedad específica de la orina.
    • ph: pH de la orina.
    • osmo: osmolaridad de la orina.
    • cond: conductivicad de la orina.
    • urea: concentración de urea en la orina.
    • calc: concentración de calcio (milimoles por litro).
  • Problema: determinar si una o más características físicas de la orina están relacionadas con la presencia de oxalatos. Además, generar un modelo capaz de clasificar pacientes con presencia de cristales que podrían ser causantes de patologías (cálculos renales).

Descripción de Modelos

  • Ambas aproximaciones (logística o probit) son útiles para modelar la probabilidad de un evento (variable dependiente) que ocurre como función de otros factores (variables independientes o predictoras).
  • Hacen parte de los Modelos Lineales Generalizados - GLM.
  • Ambas metodologías utilizan funciones de enlace - linkage que permiten variables respuesta con distribución de errores no gaussianas. Ideal para variables objetivo con distribución Poisson, Binomial, Gamma, entre otras.
    • Función logit: función de enlace con aplicaciones en regresión logística.
    • Función probit: función de enlace con aplicaciones en regresión probit. Esta función es la inversa de la función de distribución de la normal estándar.
  • La estimación de los parámetros para ambos modelos puede ser a través de Máxima Verosimilitud.
  • Ambos modelos en R pueden ser ajustados a través de la función glm().
  • Aunque las estimaciones con ambos modelos podrán ser similares, la regresión logística ha sido ampliamente utilizda en entornos epidemiológicos (ciencias de la salud), mientras que la regresión probit es común en contextos econométricos.
  • Validación de modelos: dado que el número de observaciones (filas) es bajo, se opta por implemetar Leave One Out Cross-Validation (LOOCV). Este procedimiento permitirá ajustar tantos modelos cómo número de observaciones (\(n\)). Aunque el método es costoso computacionalmente, permitirá reducir la variabilidad que se origina si se dividen aleatoriamente las observaciones únicamente en dos grupos (train y test). También es importante mencionar que el hecho de usar todos los registros puede aumentar la probabilidad de sobreentrenear el modelo (overfitting.)
    • En R se puede realizar LOOCV de cualquier modelo lineal generalizado (glm()) haciendo uso de la función cv.glm() del paquete boot. Esta función calcula el error de predicción a través de validación cruzada. Cuando el argumento K no se especifica, por defecto será igual al número de observaciones (ayuda: ?cv.glm()). Este procedimiento también es posible realizarlo manualmente por medio de loops empleando la función update().
    • Para medir el desempeño de los modelos en términos de accuracy se implementan diferentes límites (threshold) de clasificación para las probabilidades predichas. Los umbrales probados fueron 0.65, 0.6, 0.55, 0.5, 0.45 y 0.4.
  • Observación: modelar una variable dicotómica (\(y\)) con la regresión lineal clásica podría no restringir los valores de la respuesta entre 0 y 1 (ver figura 1). Además, es altamente probable que al usar este tipo de modelos se incumplan los supuestos de normalidad de los residuales.
Figura 1. Modelos Lineal, Logístico y Probit.

Regresión Logística

\[Y_i \sim\ B(p_i,\ n_i),\ para\ i=1,...,m\]

  • En la expresión anterior \(p_i\) hace referencia a la probabilidad de éxito (probabilidad de que ocurra el evento bajo estudio) y \(n_i\) determina el número de ensayos tipo Bernoulli. El número de ensayos es conocido, sin embargo, la probabilidad del éxito se desconoce.
  • Se debe cumplir que la respuesta esté acotada entre 0 y 1, es decir, que el resultado siempre será positivo, además de ser inferior a 1.
  • El exponencial (\(e\)) de cualquier valor (\(x\)) es siempre positivo y, cualquier número divivido entre la cantidad más uno (\(x+1\)) siempre será menor que 1. Bajo estas dos premisas se puede expresar la siguiente probabilidad condicional (función logística):

\[p(Y =1\ |\ X)=\frac{e^{(\beta_0+\beta_1x)}}{e^{(\beta_0+\beta_1x)}+1}\]

  • Para facilitar el cálculo escribimos \(p(Y =1\ |\ X)\) como \(p(X)\):

\[p(X) = \frac{e^{(\beta_0+\beta_1x)}}{e^{(\beta_0+\beta_1x)}+1}\\ p(e^{(\beta_0+\beta_1x)}+1) = e^{(\beta_0+\beta_1x)}\\ p \times e^{(\beta_0+\beta_1x)}\ +\ p = e^{(\beta_0+\beta_1x)}\\ p = e^{(\beta_0+\beta_1x)}\ -\ p \times e^{(\beta_0+\beta_1x)}\\ p = e^{(\beta_0+\beta_1x)}(1-p)\\ \frac{p}{1-p} = e^{(\beta_0+\beta_1x)}\]

  • Los logits (función de enlace) de las probabilidades binomiales desconocidas, es decir, los logaritmos de la razón de momios (odds ratio) son modelados como una función lineal de los \(X_i\):

\[ln(\frac{p}{1-p}) = \beta_0+\beta_1x\]

  • Esta función de enlace es conocida como sigmoide y limita su rango de probabilidades entre 0 y 1 (ver figura 2).
Figura 2. Función sigmoide.

Regresión Probit

  • La regresión probit permite analizar datos con respuesta ordinal o con distribución binomial (respuestas dicotómicas) de la forma:

\[Y_i \sim\ B(p_i,\ n_i),\ para\ i=1,...,m\]

  • El marco conceptual del modelo probit puede ser expresado de la siguiente manera:

\[p(Y = 1|X)=\ \Phi(X^{T}\beta)\]

  • Donde \(p(Y = 1|X)\) denota la probabilidad, \(\Phi\) es la función de distribución acumulativa de la distribución normal estándar y \(\beta\) son los parámetros del modelo, estimados a través de máxima verosimilitud.
  • El modelo puede ser expresado de la siguiente manera: \(Y = X^{T}\beta+\epsilon\), donde \(\epsilon \sim N(0, 1)\).
  • Las funciones logística y probit difieren en la manera como definen la función de distribución, mientras que la primera utiliza la función logística la segunda hace uso de la función de distribución acumulada de la normal estándar. Ambas funciones pueden ser comparadas en la siguiente figura:
Figura 3. Función logit y probit.

Base de Datos

Análisis Descriptivo

Positivos (1) y negativos (0)

Estadísticos descripvitos

Análisis Exploratorio

Datos ausentes

library(broom)
tidy(apply(datos, MARGIN = 2, is.na)) %>% 
  gather(key = "variable", value = "valor") %>% 
  mutate(valor = as.numeric(valor))  %>% 
  group_by(variable) %>% 
  summarise(Total_NAs = sum(valor))

Densidades

Distribución condicional

  • Las densidades condicionales son gráficos exploratorios que permiten dilucidar cómo es la probabilidad de “éxito” o “fracaso” respecto a variables numéricas. Es posible evidenciar en qué puntos (valores de x) se maximiza la probabilidad de “éxito”, que en este caso está ligado a la presencia (r = 1) de oxalatos en orina.

Boxplot comparativo

Modelos

Ajuste de modelos

# Modelos Lineales Generalizados
mod_logit  <- glm(r ~ ., data = datos, family = binomial(link = "logit"))
mod_probit <- glm(r ~ ., data = datos, family = binomial(link = "probit"))
# -------- Validación LOOCV (Manual)
out_logi_0.5 <- NULL
out_prob_0.5 <- NULL
for(i in 1:nrow(datos)){
  out_logi_0.5[i] = predict(update(mod_logit, data = datos[-i, ]),
                   newdata = datos[i,], type = "response")
  out_prob_0.5[i] = predict(update(mod_probit, data = datos[-i, ]),
                   newdata = datos[i,], type = "response")
}
# -------- Validación LOOCV (cv.glm())
## Función de coste con cutoff = 0.5
coste_0.5 <- function(r, pi = 0) mean(abs(r-pi)> 0.5)
## LOOCV
library(boot)
cv_error_logi_0.5 <- cv.glm(data = datos %>% filter(!is.na(osmo) & !is.na(cond)),
                        glmfit = mod_logit, cost = coste_0.5)
cv_error_prob_0.5 <- cv.glm(data = datos %>% filter(!is.na(osmo) & !is.na(cond)),
                        glmfit = mod_probit, cost = coste_0.5)
  • Observaciones: la ejecución automática de LOOCV con la función cv.glm() requiere una función de coste para calcular el error. El objeto devuelto por la función del paquete boot almacena el error con el nombre delta; al restar 1 menos el error (delta) se obtendrá la precisión o accuracy del modelo, que es exactamente el mismo valor obtenido manualmente.
    • Función de coste: en el código hay una función de coste o pérdida para el límite igual a 0.5. Esta función puede ser expresada de la siguiente manera: \(error = \sum |r_i - p_i| > 0.5\). Donde \(r_i\) es el i-ésimo valor real y \(p_i\) es el i-ésimo valor predicho. En el siguiente código es posible evidenciar que se obtienen los mismos resultados de forma manual y con la función cv.glm().
# Precisión manual - R. Logística (0.5)
manual_logi_0.5 <- if_else(out_logi_0.5 > 0.5, true = "1", false = "0")
manual_logi_0.5 <- mean(datos$r == manual_logi_0.5, na.rm = TRUE)
# Precisión con cv.glm() - R. Logística (0.5)
cv_logi_0.5 <- 1 - cv_error_logi_0.5$delta[1]
# Precisión manual - R. Probit (0.5)
manual_prob_0.5 <- if_else(out_prob_0.5 > 0.5, true = "1", false = "0")
manual_prob_0.5 <- mean(datos$r == manual_prob_0.5, na.rm = TRUE)
# Precisión con cv.glm() - R. Logística (0.5)
cv_prob_0.5 <- 1 - cv_error_prob_0.5$delta[1]
# Imprimiendo resultados
cat(paste0("R. Logística 0.5 Manual = ", manual_logi_0.5),
    "y R. Logística 0.5 cv.glm() = ", cv_logi_0.5)
R. Logística 0.5 Manual = 0.74025974025974 y R. Logística 0.5 cv.glm() =  0.7402597

Resumen R. Logística

tidy(mod_logit) %>% 
  select(term, estimate, p.value) %>% 
  mutate(signif = if_else(p.value <= 0.05, true = "Significativo",
                          false = "No significativo"))

Resumen R. Probit

tidy(mod_probit) %>% 
  select(term, estimate, p.value) %>% 
  mutate(signif = if_else(p.value <= 0.05, true = "Significativo",
                          false = "No significativo"))

Comparación de Modelos

  • Tabla:
# Punto de corte 0.65 - clases 0 y 1
out_logi_0.65 <- if_else(condition = out_logi > 0.65, true = "1", false = "0")
out_prob_0.65 <- if_else(condition = out_prob > 0.65, true = "1", false = "0")
# Punto de corte 0.6 - clases 0 y 1
out_logi_0.6 <- if_else(condition = out_logi > 0.6, true = "1", false = "0")
out_prob_0.6 <- if_else(condition = out_prob > 0.6, true = "1", false = "0")
# Punto de corte 0.55 - clases 0 y 1
out_logi_0.55 <- if_else(condition = out_logi > 0.55, true = "1", false = "0")
out_prob_0.55 <- if_else(condition = out_prob > 0.55, true = "1", false = "0")
# Punto de corte 0.5 - clases 0 y 1
out_logi_0.5 <- if_else(condition = out_logi > 0.5, true = "1", false = "0")
out_prob_0.5 <- if_else(condition = out_prob > 0.5, true = "1", false = "0")
# Punto de corte 0.45 - clases 0 y 1
out_logi_0.45 <- if_else(condition = out_logi > 0.45, true = "1", false = "0")
out_prob_0.45 <- if_else(condition = out_prob > 0.45, true = "1", false = "0")
# Punto de corte 0.4 - clases 0 y 1
out_logi_0.4 <- if_else(condition = out_logi > 0.4, true = "1", false = "0")
out_prob_0.4 <- if_else(condition = out_prob > 0.4, true = "1", false = "0")
# Error de modelos
df_accuracy <- data.frame(
  Modelo = c("R. Logística - (0.65)", "R. Probit - (0.65)",
             "R. Logística - (0.6)", "R. Probit - (0.6)",
             "R. Logística - (0.55)", "R. Probit - (0.55)",
             "R. Logística - (0.5)", "R. Probit - (0.5)",
             "R. Logística - (0.45)", "R. Probit - (0.45)",
             "R. Logística - (0.4)", "R. Probit - (0.4)"),
  Accucary = c(mean(datos$r == out_logi_0.65, na.rm = TRUE),
               mean(datos$r == out_prob_0.65, na.rm = TRUE),
               mean(datos$r == out_logi_0.6, na.rm = TRUE),
               mean(datos$r == out_prob_0.6, na.rm = TRUE),
               mean(datos$r == out_logi_0.55, na.rm = TRUE),
               mean(datos$r == out_prob_0.55, na.rm = TRUE),
               mean(datos$r == out_logi_0.5, na.rm = TRUE),
               mean(datos$r == out_prob_0.5, na.rm = TRUE),
               mean(datos$r == out_logi_0.45, na.rm = TRUE),
               mean(datos$r == out_prob_0.45, na.rm = TRUE),
               mean(datos$r == out_logi_0.4, na.rm = TRUE),
               mean(datos$r == out_prob_0.4, na.rm = TRUE))
) %>% 
  separate(Modelo, into = c("Modelo", "Límite"), remove = FALSE, sep = "-")
df_accuracy
  • Gráfico:

Modelo Final

Conclusiones

  • Ambos modelos presentan desempeños similares en la detección de oxalatos en orina. Para el modelo logístico son estadísticamente significativas las variables urea (\(p=0.047025469\)) y calcio (\(p=0.001211312\)), no obstante, para el modelo probit sólo es significativa la variable calcio (\(p = 0.0004386245\)).
  • El signo de los coeficientes estimados para ambos modelos es el mismo. Esto indica que ambas aproximaciones poseen similitud en cuanto a la relación de cambio, sin embargo, la magnitud estimada es diferente.
  • Se observa diferencia en la precisión de los modelos para puntos de corte (cutoff) distintos, obteniendo las mejores predicciones con límites iguales a 0.6 y 0.65. La precisión es más baja con ambos modelos cuando se emplean umbrales iguales a 0.45 y 0.5

Modelo Logístico

  • En términos logarítmicos el modelo puede ser expresado de la siguiente manera:

\[logit(oxalato = 1) = -355.3377 + 355.9437\times grav - 0.4957\times pH +\\ 0.0168\times osmo - 0.4328\times conduc - 0.0320\times urea + 0.7836 \times calcio\]

  • Si se quiere calcular la probabilidad para cualquier combinación de valores de predictoras, se puede obtener de la siguiente manera:

\[p(oxalato = 1) = \frac{e^{-355.3377 + 355.9437\times grav - 0.4957\times pH + 0.0168\times osmo - 0.4328\times conduc - 0.0320\times urea + 0.7836 \times calcio}}{1 + e^{-355.3377 + 355.9437\times grav - 0.4957\times pH + 0.0168\times osmo - 0.4328\times conduc - 0.0320\times urea + 0.7836 \times calcio}}\]

LS0tCnRpdGxlOiAiRGV0ZWNjacOzbiBkZSBPeGFsYXRvcyBlbiBPcmluYSIKc3VidGl0bGU6ICJSZWdyZXNpw7NuIExvZ8Otc3RpY2EgeSBSZWdyZXNpw7NuIFByb2JpdCIKYXV0aG9yOiAiRWRpbWVyIERhdmlkIEphcmFtaWxsbyIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICBjc3M6IGVzdGlsby5jc3MKICAgIHRoZW1lOiBjb3NtbwogICAgaGlnaGxpZ2h0OiB6ZW5idXJuCiAgICBkZl9wcmludDogcGFnZWQKICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6CiAgICAgIHNtb290aF9zY3JvbGw6IGZhbHNlCiAgICAgIGNvbGxhcHNlZDogZmFsc2UKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgIHdhcm5pbmcgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgIGVycm9yID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICBmaWcuYWxpZ24gPSAiY2VudGVyIiwKICAgICAgICAgICAgICAgICAgICAgIGZpZy53aWR0aCA9IDguNSwKICAgICAgICAgICAgICAgICAgICAgIGZpZy5oZWlnaHQgPSA1LAogICAgICAgICAgICAgICAgICAgICAgY29sbGFwc2UgPSBUUlVFKQpgYGAKCjxjZW50ZXI+CjxpbWcgc3JjPSJpbWFnZXMvaW1hZ2UucG5nIiB3aWR0aCA9ICI0ODAiIGhlaWdodD0iMzIwIj4KPC9jZW50ZXI+CgojIEZ1ZW50ZSBkZSBpbmZvcm1hY2nDs24KCi0gW0Z1ZW50ZTogQ1JBTiBEYXRhc2V0cyAtIFVyaW5lIEFuYWx5c2lzIERhdGEuXShodHRwczovL3ZpbmNlbnRhcmVsYnVuZG9jay5naXRodWIuaW8vUmRhdGFzZXRzL2RvYy9ib290L3VyaW5lLmh0bWwpCi0gKipEZXNjcmlwY2nDs246KiogcmVnaXN0cm9zIGNvbiBpbmZvcm1hY2nDs24gZGUgNzkgbXVlc3RyYXMgZGUgb3JpbmEgY29uIGNhcmFjdGVyw61zdGljYXMgZsOtc2ljYXMuCi0gKipWYXJpYWJsZXM6KiogZXN0w6FuIHJlZ2lzdHJhZGFzIGxhcyBzaWd1aWVudGVzIHZhcmlhYmxlczoKICAgIC0gKipgcmAqKjogaW5kaWNhZG9yIGRlIHByZXNlbmNpYSBkZSBveGFsYXRvIGRlIGNhbGNpby4gPHRyZWQ+KFZhcmlhYmxlIE9iamV0aXZvKTwvdHJlZD4KICAgICAgICAtICoqYDBgOioqIGF1c2VuY2lhIGRlIG94YWxhdG8gZW4gb3JpbmEuCiAgICAgICAgLSAqKmAxYDoqKiBwcmVzZW5jaWEgZGUgb3hhbGF0byBlbiBvcmluYS4KICAgIC0gKipgZ3Jhdml0eWAqKjogZ3JhdmVkYWQgZXNwZWPDrWZpY2EgZGUgbGEgb3JpbmEuCiAgICAtICoqYHBoYCoqOiBwSCBkZSBsYSBvcmluYS4KICAgIC0gKipgb3Ntb2AqKjogb3Ntb2xhcmlkYWQgZGUgbGEgb3JpbmEuCiAgICAtICoqYGNvbmRgKio6IGNvbmR1Y3RpdmljYWQgZGUgbGEgb3JpbmEuCiAgICAtICoqYHVyZWFgKio6IGNvbmNlbnRyYWNpw7NuIGRlIHVyZWEgZW4gbGEgb3JpbmEuCiAgICAtICoqYGNhbGNgKio6IGNvbmNlbnRyYWNpw7NuIGRlIGNhbGNpbyAobWlsaW1vbGVzIHBvciBsaXRybykuCi0gKipQcm9ibGVtYToqKiBkZXRlcm1pbmFyIHNpIHVuYSBvIG3DoXMgY2FyYWN0ZXLDrXN0aWNhcyBmw61zaWNhcyBkZSBsYSBvcmluYSBlc3TDoW4gcmVsYWNpb25hZGFzIGNvbiBsYSBwcmVzZW5jaWEgZGUgb3hhbGF0b3MuIEFkZW3DoXMsIGdlbmVyYXIgdW4gbW9kZWxvIGNhcGF6IGRlIGNsYXNpZmljYXIgcGFjaWVudGVzIGNvbiBwcmVzZW5jaWEgZGUgY3Jpc3RhbGVzIHF1ZSBwb2Ryw61hbiBzZXIgY2F1c2FudGVzIGRlIHBhdG9sb2fDrWFzIChjw6FsY3Vsb3MgcmVuYWxlcykuCgojIERlc2NyaXBjacOzbiBkZSBNb2RlbG9zCgotIEFtYmFzIGFwcm94aW1hY2lvbmVzICgqbG9nw61zdGljYSogbyAqcHJvYml0Kikgc29uIMO6dGlsZXMgcGFyYSBtb2RlbGFyIGxhIHByb2JhYmlsaWRhZCBkZSB1biBldmVudG8gKHZhcmlhYmxlIGRlcGVuZGllbnRlKSBxdWUgb2N1cnJlIGNvbW8gZnVuY2nDs24gZGUgb3Ryb3MgZmFjdG9yZXMgKHZhcmlhYmxlcyBpbmRlcGVuZGllbnRlcyBvIHByZWRpY3RvcmFzKS4KLSBIYWNlbiBwYXJ0ZSBkZSBsb3MgWypNb2RlbG9zIExpbmVhbGVzIEdlbmVyYWxpemFkb3MgLSBHTE0qXShodHRwczovL2VzLndpa2lwZWRpYS5vcmcvd2lraS9Nb2RlbG9fbGluZWFsX2dlbmVyYWxpemFkbykuCi0gQW1iYXMgbWV0b2RvbG9nw61hcyB1dGlsaXphbiBmdW5jaW9uZXMgZGUgZW5sYWNlIC0gKmxpbmthZ2UqIHF1ZSBwZXJtaXRlbiB2YXJpYWJsZXMgcmVzcHVlc3RhIGNvbiBkaXN0cmlidWNpw7NuIGRlIGVycm9yZXMgbm8gZ2F1c3NpYW5hcy4gSWRlYWwgcGFyYSB2YXJpYWJsZXMgb2JqZXRpdm8gY29uIGRpc3RyaWJ1Y2nDs24gKlBvaXNzb24qLCAqQmlub21pYWwqLCAqR2FtbWEqLCBlbnRyZSBvdHJhcy4KICAgIC0gWypGdW5jacOzbiBsb2dpdDoqXShodHRwczovL2VzLndpa2lwZWRpYS5vcmcvd2lraS9Mb2dpdCkgZnVuY2nDs24gZGUgZW5sYWNlIGNvbiBhcGxpY2FjaW9uZXMgZW4gcmVncmVzacOzbiBsb2fDrXN0aWNhLgogICAgLSBbKkZ1bmNpw7NuIHByb2JpdDoqXShodHRwczovL2VzLndpa2lwZWRpYS5vcmcvd2lraS9GdW5jaSVDMyVCM25fcHJvYml0KSBmdW5jacOzbiBkZSBlbmxhY2UgY29uIGFwbGljYWNpb25lcyBlbiByZWdyZXNpw7NuIHByb2JpdC4gRXN0YSBmdW5jacOzbiBlcyBsYSBpbnZlcnNhIGRlIGxhIGZ1bmNpw7NuIGRlIGRpc3RyaWJ1Y2nDs24gZGUgbGEgbm9ybWFsIGVzdMOhbmRhci4KLSBMYSBlc3RpbWFjacOzbiBkZSBsb3MgcGFyw6FtZXRyb3MgcGFyYSBhbWJvcyBtb2RlbG9zIHB1ZWRlIHNlciBhIHRyYXbDqXMgZGUgWypNw6F4aW1hIFZlcm9zaW1pbGl0dWQuKl0oaHR0cHM6Ly9lcy53aWtpcGVkaWEub3JnL3dpa2kvTSVDMyVBMXhpbWFfdmVyb3NpbWlsaXR1ZCkKLSBBbWJvcyBtb2RlbG9zIGVuICpSKiBwdWVkZW4gc2VyIGFqdXN0YWRvcyBhIHRyYXbDqXMgZGUgbGEgZnVuY2nDs24gYGdsbSgpYC4KLSBBdW5xdWUgbGFzIGVzdGltYWNpb25lcyBjb24gYW1ib3MgbW9kZWxvcyBwb2Ryw6FuIHNlciBzaW1pbGFyZXMsIGxhIHJlZ3Jlc2nDs24gbG9nw61zdGljYSBoYSBzaWRvIGFtcGxpYW1lbnRlIHV0aWxpemRhIGVuIGVudG9ybm9zIGVwaWRlbWlvbMOzZ2ljb3MgKGNpZW5jaWFzIGRlIGxhIHNhbHVkKSwgbWllbnRyYXMgcXVlIGxhIHJlZ3Jlc2nDs24gcHJvYml0IGVzIGNvbcO6biBlbiBjb250ZXh0b3MgZWNvbm9tw6l0cmljb3MuCi0gKipWYWxpZGFjacOzbiBkZSBtb2RlbG9zOioqIGRhZG8gcXVlIGVsIG7Dum1lcm8gZGUgb2JzZXJ2YWNpb25lcyAoZmlsYXMpIGVzIGJham8sIHNlIG9wdGEgcG9yIGltcGxlbWV0YXIgWypMZWF2ZSBPbmUgT3V0IENyb3NzLVZhbGlkYXRpb24gKExPT0NWKSpdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0Nyb3NzLXZhbGlkYXRpb25fKHN0YXRpc3RpY3MpI0V4aGF1c3RpdmVfY3Jvc3MtdmFsaWRhdGlvbikuIEVzdGUgcHJvY2VkaW1pZW50byBwZXJtaXRpcsOhIGFqdXN0YXIgdGFudG9zIG1vZGVsb3MgY8OzbW8gbsO6bWVybyBkZSBvYnNlcnZhY2lvbmVzICgkbiQpLiBBdW5xdWUgZWwgbcOpdG9kbyBlcyBjb3N0b3NvIGNvbXB1dGFjaW9uYWxtZW50ZSwgcGVybWl0aXLDoSByZWR1Y2lyIGxhIHZhcmlhYmlsaWRhZCBxdWUgc2Ugb3JpZ2luYSBzaSBzZSBkaXZpZGVuIGFsZWF0b3JpYW1lbnRlIGxhcyBvYnNlcnZhY2lvbmVzIMO6bmljYW1lbnRlIGVuIGRvcyBncnVwb3MgKCp0cmFpbiogeSAqdGVzdCopLiBUYW1iacOpbiBlcyBpbXBvcnRhbnRlIG1lbmNpb25hciBxdWUgZWwgaGVjaG8gZGUgdXNhciB0b2RvcyBsb3MgcmVnaXN0cm9zIHB1ZWRlIGF1bWVudGFyIGxhIHByb2JhYmlsaWRhZCBkZSBzb2JyZWVudHJlbmVhciBlbCBtb2RlbG8gKFsqb3ZlcmZpdHRpbmcuKl0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvT3ZlcmZpdHRpbmcpKQogICAgLSBFbiBSIHNlIHB1ZWRlIHJlYWxpemFyICpMT09DViogZGUgY3VhbHF1aWVyIG1vZGVsbyBsaW5lYWwgZ2VuZXJhbGl6YWRvIChgZ2xtKClgKSBoYWNpZW5kbyB1c28gZGUgbGEgZnVuY2nDs24gYGN2LmdsbSgpYCBkZWwgcGFxdWV0ZSBgYm9vdGAuIEVzdGEgZnVuY2nDs24gY2FsY3VsYSBlbCBlcnJvciBkZSBwcmVkaWNjacOzbiBhIHRyYXbDqXMgZGUgKnZhbGlkYWNpw7NuIGNydXphZGEqLiBDdWFuZG8gZWwgYXJndW1lbnRvIGBLYCBubyBzZSBlc3BlY2lmaWNhLCBwb3IgZGVmZWN0byBzZXLDoSBpZ3VhbCBhbCBuw7ptZXJvIGRlIG9ic2VydmFjaW9uZXMgKFtheXVkYTogP2N2LmdsbSgpXShodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMvYm9vdC92ZXJzaW9ucy8xLjMtMjMvdG9waWNzL2N2LmdsbSkpLiBFc3RlIHByb2NlZGltaWVudG8gdGFtYmnDqW4gZXMgcG9zaWJsZSByZWFsaXphcmxvIG1hbnVhbG1lbnRlIHBvciBtZWRpbyBkZSA8dHJlZD4qbG9vcHMqPC90cmVkPiBlbXBsZWFuZG8gbGEgZnVuY2nDs24gYHVwZGF0ZSgpYC4KICAgIC0gUGFyYSBtZWRpciBlbCBkZXNlbXBlw7FvIGRlIGxvcyBtb2RlbG9zIGVuIHTDqXJtaW5vcyBkZSAqYWNjdXJhY3kqIHNlIGltcGxlbWVudGFuIGRpZmVyZW50ZXMgbMOtbWl0ZXMgKFsqdGhyZXNob2xkKl0oaHR0cHM6Ly9zdGF0cy5zdGFja2V4Y2hhbmdlLmNvbS9xdWVzdGlvbnMvMzEyMTE5L2NsYXNzaWZpY2F0aW9uLXByb2JhYmlsaXR5LXRocmVzaG9sZCkpIGRlIGNsYXNpZmljYWNpw7NuIHBhcmEgbGFzIHByb2JhYmlsaWRhZGVzIHByZWRpY2hhcy4gTG9zIHVtYnJhbGVzIHByb2JhZG9zIGZ1ZXJvbiAwLjY1LCAwLjYsIDAuNTUsIDAuNSwgMC40NSB5IDAuNC4KLSA8dHJlZD4qKk9ic2VydmFjacOzbjoqKjwvdHJlZD4gbW9kZWxhciB1bmEgdmFyaWFibGUgZGljb3TDs21pY2EgKCR5JCkgY29uIGxhIHJlZ3Jlc2nDs24gbGluZWFsIGNsw6FzaWNhIHBvZHLDrWEgbm8gcmVzdHJpbmdpciBsb3MgdmFsb3JlcyBkZSBsYSByZXNwdWVzdGEgZW50cmUgMCB5IDEgICh2ZXIgZmlndXJhIDEpLiBBZGVtw6FzLCBlcyBhbHRhbWVudGUgcHJvYmFibGUgcXVlIGFsIHVzYXIgZXN0ZSB0aXBvIGRlIG1vZGVsb3Mgc2UgaW5jdW1wbGFuIGxvcyBzdXB1ZXN0b3MgZGUgbm9ybWFsaWRhZCBkZSBsb3MgcmVzaWR1YWxlcy4KCjxjZW50ZXI+CjxpbWcgc3JjPSJpbWFnZXMvbG9nMi5qcGciIHdpZHRoID0gIjU4MCIgaGVpZ2h0PSI0MjAiPgo8ZmlnY2FwdGlvbj5GaWd1cmEgMS4gTW9kZWxvcyBMaW5lYWwsIExvZ8Otc3RpY28geSBQcm9iaXQuPC9maWdjYXB0aW9uPgo8L2NlbnRlcj4KCiMjIyBbUmVncmVzacOzbiBMb2fDrXN0aWNhXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9Mb2dpc3RpY19yZWdyZXNzaW9uKQoKLSBMYSByZWdyZXNpw7NuIGxvZ8Otc3RpY2EgYW5hbGl6YSBkYXRvcyBjb24gWypkaXN0cmlidWNpw7NuIGJpbm9taWFsKl0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvQmlub21pYWxfZGlzdHJpYnV0aW9uKSBkZSBsYSBmb3JtYToKCiQkWV9pIFxzaW1cIEIocF9pLFwgbl9pKSxcIHBhcmFcIGk9MSwuLi4sbSQkCgotIEVuIGxhIGV4cHJlc2nDs24gYW50ZXJpb3IgJHBfaSQgaGFjZSByZWZlcmVuY2lhIGEgbGEgcHJvYmFiaWxpZGFkIGRlIMOpeGl0byAocHJvYmFiaWxpZGFkIGRlIHF1ZSBvY3VycmEgZWwgZXZlbnRvIGJham8gZXN0dWRpbykgeSAkbl9pJCBkZXRlcm1pbmEgZWwgbsO6bWVybyBkZSBlbnNheW9zIHRpcG8gKkJlcm5vdWxsaSouIEVsIG7Dum1lcm8gZGUgZW5zYXlvcyBlcyBjb25vY2lkbywgc2luIGVtYmFyZ28sIGxhIHByb2JhYmlsaWRhZCBkZWwgw6l4aXRvIHNlIGRlc2Nvbm9jZS4KLSBTZSBkZWJlIGN1bXBsaXIgcXVlIGxhIHJlc3B1ZXN0YSBlc3TDqSBhY290YWRhIGVudHJlIDAgeSAxLCBlcyBkZWNpciwgcXVlIGVsIHJlc3VsdGFkbyBzaWVtcHJlIHNlcsOhIHBvc2l0aXZvLCBhZGVtw6FzIGRlIHNlciBpbmZlcmlvciBhIDEuCi0gRWwgZXhwb25lbmNpYWwgKCRlJCkgZGUgY3VhbHF1aWVyIHZhbG9yICgkeCQpIGVzIHNpZW1wcmUgcG9zaXRpdm8geSwgY3VhbHF1aWVyIG7Dum1lcm8gZGl2aXZpZG8gZW50cmUgbGEgY2FudGlkYWQgbcOhcyB1bm8gKCR4KzEkKSBzaWVtcHJlIHNlcsOhIG1lbm9yIHF1ZSAxLiBCYWpvIGVzdGFzIGRvcyBwcmVtaXNhcyBzZSBwdWVkZSBleHByZXNhciBsYSBzaWd1aWVudGUgcHJvYmFiaWxpZGFkIGNvbmRpY2lvbmFsIChmdW5jacOzbiBsb2fDrXN0aWNhKToKCiQkcChZID0xXCB8XCBYKT1cZnJhY3tlXnsoXGJldGFfMCtcYmV0YV8xeCl9fXtlXnsoXGJldGFfMCtcYmV0YV8xeCl9KzF9JCQKCi0gUGFyYSBmYWNpbGl0YXIgZWwgY8OhbGN1bG8gZXNjcmliaW1vcyAkcChZID0xXCB8XCBYKSQgY29tbyAkcChYKSQ6CgokJHAoWCkgPSBcZnJhY3tlXnsoXGJldGFfMCtcYmV0YV8xeCl9fXtlXnsoXGJldGFfMCtcYmV0YV8xeCl9KzF9XFwKcChlXnsoXGJldGFfMCtcYmV0YV8xeCl9KzEpID0gZV57KFxiZXRhXzArXGJldGFfMXgpfVxcCnAgXHRpbWVzIGVeeyhcYmV0YV8wK1xiZXRhXzF4KX1cICtcIHAgPSBlXnsoXGJldGFfMCtcYmV0YV8xeCl9XFwKcCA9IGVeeyhcYmV0YV8wK1xiZXRhXzF4KX1cIC1cIHAgXHRpbWVzIGVeeyhcYmV0YV8wK1xiZXRhXzF4KX1cXApwID0gZV57KFxiZXRhXzArXGJldGFfMXgpfSgxLXApXFwKXGZyYWN7cH17MS1wfSA9IGVeeyhcYmV0YV8wK1xiZXRhXzF4KX0kJAoKLSBMb3MgKmxvZ2l0cyogKGZ1bmNpw7NuIGRlIGVubGFjZSkgZGUgbGFzIHByb2JhYmlsaWRhZGVzIGJpbm9taWFsZXMgZGVzY29ub2NpZGFzLCBlcyBkZWNpciwgbG9zIGxvZ2FyaXRtb3MgZGUgbGEgWypyYXrDs24gZGUgbW9taW9zIChvZGRzIHJhdGlvKSpdKGh0dHBzOi8vZXMud2lraXBlZGlhLm9yZy93aWtpL1JheiVDMyVCM25fZGVfbW9taW9zKSBzb24gbW9kZWxhZG9zIGNvbW8gdW5hIGZ1bmNpw7NuIGxpbmVhbCBkZSBsb3MgJFhfaSQ6CgokJGxuKFxmcmFje3B9ezEtcH0pID0gXGJldGFfMCtcYmV0YV8xeCQkCgotIEVzdGEgZnVuY2nDs24gZGUgZW5sYWNlIGVzIGNvbm9jaWRhIGNvbW8gKnNpZ21vaWRlKiB5IGxpbWl0YSBzdSByYW5nbyBkZSBwcm9iYWJpbGlkYWRlcyBlbnRyZSAwIHkgMSAodmVyIGZpZ3VyYSAyKS4KCjxjZW50ZXI+CjxpbWcgc3JjPSJpbWFnZXMvc2lnbW9pZC5wbmciIHdpZHRoID0gIjU4MCIgaGVpZ2h0PSI0MjAiPgo8ZmlnY2FwdGlvbj5GaWd1cmEgMi4gRnVuY2nDs24gc2lnbW9pZGUuPC9maWdjYXB0aW9uPgo8L2NlbnRlcj4KCiMjIyBbUmVncmVzacOzbiBQcm9iaXRdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1Byb2JpdF9tb2RlbCkKCi0gTGEgcmVncmVzacOzbiBwcm9iaXQgcGVybWl0ZSBhbmFsaXphciBkYXRvcyBjb24gcmVzcHVlc3RhIG9yZGluYWwgbyBjb24gKmRpc3RyaWJ1Y2nDs24gYmlub21pYWwqIChyZXNwdWVzdGFzIGRpY290w7NtaWNhcykgZGUgbGEgZm9ybWE6CgokJFlfaSBcc2ltXCBCKHBfaSxcIG5faSksXCBwYXJhXCBpPTEsLi4uLG0kJAoKLSBFbCBtYXJjbyBjb25jZXB0dWFsIGRlbCBtb2RlbG8gcHJvYml0IHB1ZWRlIHNlciBleHByZXNhZG8gZGUgbGEgc2lndWllbnRlIG1hbmVyYToKCiQkcChZID0gMXxYKT1cIFxQaGkoWF57VH1cYmV0YSkkJAoKLSBEb25kZSAkcChZID0gMXxYKSQgZGVub3RhIGxhIHByb2JhYmlsaWRhZCwgJFxQaGkkIGVzIGxhIGZ1bmNpw7NuIGRlIGRpc3RyaWJ1Y2nDs24gYWN1bXVsYXRpdmEgZGUgbGEgZGlzdHJpYnVjacOzbiBub3JtYWwgZXN0w6FuZGFyIHkgJFxiZXRhJCBzb24gbG9zIHBhcsOhbWV0cm9zIGRlbCBtb2RlbG8sIGVzdGltYWRvcyBhIHRyYXbDqXMgZGUgbcOheGltYSB2ZXJvc2ltaWxpdHVkLgotIEVsIG1vZGVsbyBwdWVkZSBzZXIgZXhwcmVzYWRvIGRlIGxhIHNpZ3VpZW50ZSBtYW5lcmE6ICRZID0gWF57VH1cYmV0YStcZXBzaWxvbiQsIGRvbmRlICRcZXBzaWxvbiBcc2ltIE4oMCwgMSkkLgotIExhcyBmdW5jaW9uZXMgbG9nw61zdGljYSB5IHByb2JpdCBkaWZpZXJlbiBlbiBsYSBtYW5lcmEgY29tbyBkZWZpbmVuIGxhIGZ1bmNpw7NuIGRlIGRpc3RyaWJ1Y2nDs24sIG1pZW50cmFzIHF1ZSBsYSBwcmltZXJhIHV0aWxpemEgbGEgZnVuY2nDs24gbG9nw61zdGljYSBsYSBzZWd1bmRhIGhhY2UgdXNvIGRlIGxhIGZ1bmNpw7NuIGRlIGRpc3RyaWJ1Y2nDs24gYWN1bXVsYWRhIGRlIGxhIG5vcm1hbCBlc3TDoW5kYXIuIEFtYmFzIGZ1bmNpb25lcyBwdWVkZW4gc2VyIGNvbXBhcmFkYXMgZW4gbGEgc2lndWllbnRlIGZpZ3VyYToKCjxjZW50ZXI+CjxpbWcgc3JjPSJpbWFnZXMvbG9naXRfcHJvYml0My5wbmciIHdpZHRoID0gIjUzMCIgaGVpZ2h0PSI0MDAiPgo8ZmlnY2FwdGlvbj5GaWd1cmEgMy4gRnVuY2nDs24gbG9naXQgeSBwcm9iaXQuPC9maWdjYXB0aW9uPgo8L2NlbnRlcj4KCiMgQmFzZSBkZSBEYXRvcwoKYGBge3J9CiMgSW1wb3J0YW5kbyBkYXRvcwpsaWJyYXJ5KGRwbHlyKQpkYXRvcyA8LSByZWFkLmNzdigiRGF0b3MvT3JpbmEuY3N2IikgJT4lIAogIHNlbGVjdCgtWCkKCiMgQ29udmVyc2nDs24gZGUgdmFyaWFibGUgb2JqZXRpdm8gYSBmYWN0b3IKZGF0b3MkciA8LSBhcy5mYWN0b3IoZGF0b3MkcikKCiMgSW1wcmltaWVuZG8gZGF0b3MKZGF0b3MKYGBgCgojIEFuw6FsaXNpcyBEZXNjcmlwdGl2bwoKIyMjIFBvc2l0aXZvcyAoYDFgKSB5IG5lZ2F0aXZvcyAoYDBgKQoKYGBge3J9CmxpYnJhcnkoRFQpCmRhdG9zICU+JSAKICBncm91cF9ieShyKSAlPiUgCiAgc3VtbWFyaXNlKFRvdGFsID0gbigpKSAlPiUgCiAgdW5ncm91cCgpICU+JSAKICBtdXRhdGUoYEYuIFJlbGF0aXZhYCA9IHJvdW5kKFRvdGFsL3N1bShUb3RhbCksIGRpZ2l0cyA9IDIpLAogICAgICAgICBQcm9wb3JjacOzbiA9IHBhc3RlMChgRi4gUmVsYXRpdmFgICogMTAwLCAiJSIpKSAlPiUgCiAgcmVuYW1lKE94YWxhdG8gPSByKQpgYGAKCiMjIyBFc3RhZMOtc3RpY29zIGRlc2NyaXB2aXRvcwoKYGBge3J9CmxpYnJhcnkodGlkeXIpCmRhdG9zICU+JSAKICBnYXRoZXIoa2V5ID0gInZhcmlhYmxlIiwgdmFsdWUgPSAidmFsb3IiLCAtcikgJT4lIAogIGdyb3VwX2J5KHZhcmlhYmxlLCByKSAlPiUgCiAgc3VtbWFyaXNlKFByb21lZGlvID0gcm91bmQobWVhbih2YWxvciwgbmEucm0gPSBUUlVFKSwgZGlnaXRzID0gMiksCiAgICAgICAgICAgIGBELiBFc3TDoW5kYXJgID0gcm91bmQoc2QodmFsb3IsIG5hLnJtID0gVFJVRSksIGRpZ2l0cyA9IDIpLAogICAgICAgICAgICBNw61uaW1vID0gcm91bmQobWluKHZhbG9yLCBuYS5ybSA9IFRSVUUpLCBkaWdpdHMgPSAyKSwKICAgICAgICAgICAgTcOheGltbyA9IHJvdW5kKG1heCh2YWxvciwgbmEucm0gPSBUUlVFKSwgZGlnaXRzID0gMiksCiAgICAgICAgICAgIFExID0gcm91bmQocXVhbnRpbGUodmFsb3IsIHByb2IgPSAwLjI1LCBuYS5ybSA9IFRSVUUpLCBkaWdpdHMgPSAyKSwKICAgICAgICAgICAgUTIgPSByb3VuZChxdWFudGlsZSh2YWxvciwgcHJvYiA9IDAuNSwgbmEucm0gPSBUUlVFKSwgZGlnaXRzID0gMiksCiAgICAgICAgICAgIFEzID0gcm91bmQocXVhbnRpbGUodmFsb3IsIHByb2IgPSAwLjc1LCBuYS5ybSA9IFRSVUUpLCBkaWdpdHMgPSAyKSkgJT4lIAogIHJlbmFtZShPeGFsYXRvID0gciwgVmFyaWFibGUgPSB2YXJpYWJsZSkgJT4lIAogIGRhdGF0YWJsZSgpCmBgYAoKCiMgQW7DoWxpc2lzIEV4cGxvcmF0b3JpbwoKIyMjIERhdG9zIGF1c2VudGVzCgpgYGB7ciwgZWNobz1UUlVFfQpsaWJyYXJ5KGJyb29tKQp0aWR5KGFwcGx5KGRhdG9zLCBNQVJHSU4gPSAyLCBpcy5uYSkpICU+JSAKICBnYXRoZXIoa2V5ID0gInZhcmlhYmxlIiwgdmFsdWUgPSAidmFsb3IiKSAlPiUgCiAgbXV0YXRlKHZhbG9yID0gYXMubnVtZXJpYyh2YWxvcikpICAlPiUgCiAgZ3JvdXBfYnkodmFyaWFibGUpICU+JSAKICBzdW1tYXJpc2UoVG90YWxfTkFzID0gc3VtKHZhbG9yKSkKYGBgCgojIyMgRGVuc2lkYWRlcwoKYGBge3IsIGZpZy5oZWlnaHQ9NX0KbGlicmFyeShnZ3Bsb3QyKQpjb2xvcmVzIDwtIGMoImRvZGdlcmJsdWUiLCAiZ3JheTQwIikKZGF0b3MgJT4lIAogIHJlbmFtZShPeGFsYXRvID0gcikgJT4lIAogIGdhdGhlcihrZXkgPSAidmFyaWFibGUiLCB2YWx1ZSA9ICJ2YWxvciIsIC1PeGFsYXRvKSAlPiUgCiAgZ2dwbG90KGRhdGEgPSAuLCBhZXMoeCA9IHZhbG9yLCBmaWxsID0gT3hhbGF0bykpICsKICBmYWNldF93cmFwKH52YXJpYWJsZSwgc2NhbGVzID0gImZyZWUiKSArCiAgZ2VvbV9kZW5zaXR5KGFscGhhID0gMC43KSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gY29sb3JlcykgKwogIGxhYnMoeCA9ICIiLCB5ID0gIiIpICsKICB0aGVtZV9saWdodCgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikKYGBgCgojIyMgRGlzdHJpYnVjacOzbiBjb25kaWNpb25hbAoKLSBMYXMgZGVuc2lkYWRlcyBjb25kaWNpb25hbGVzIHNvbiBncsOhZmljb3MgZXhwbG9yYXRvcmlvcyBxdWUgcGVybWl0ZW4gZGlsdWNpZGFyIGPDs21vIGVzIGxhIHByb2JhYmlsaWRhZCBkZSAiw6l4aXRvIiBvICJmcmFjYXNvIiByZXNwZWN0byBhIHZhcmlhYmxlcyBudW3DqXJpY2FzLiBFcyBwb3NpYmxlIGV2aWRlbmNpYXIgZW4gcXXDqSBwdW50b3MgKHZhbG9yZXMgZGUgeCkgc2UgbWF4aW1pemEgbGEgcHJvYmFiaWxpZGFkIGRlICLDqXhpdG8iLCBxdWUgZW4gZXN0ZSBjYXNvIGVzdMOhIGxpZ2FkbyBhIGxhIHByZXNlbmNpYSAoPHRyZWQ+ciA9IDE8L3RyZWQ+KSBkZSBveGFsYXRvcyBlbiBvcmluYS4KCmBgYHtyfQpwYXIobWZyb3cgPSBjKDIsIDMpKQpjZHBsb3QoZGF0b3MkciB+IGRhdG9zJGdyYXZpdHksIHhsYWIgPSAiR3JhdmVkYWQgZXNwZWPDrWZpY2EiLAogICAgICAgeWxhYiA9ICJPeGFsYXRvIiwgY29sID0gY29sb3JlcykKY2RwbG90KGRhdG9zJHIgfiBkYXRvcyRwaCwgeGxhYiA9ICJwSCIsCiAgICAgICB5bGFiID0gIk94YWxhdG8iLCBjb2wgPSBjb2xvcmVzKQpjZHBsb3QoZGF0b3MkciB+IGRhdG9zJG9zbW8sIHhsYWIgPSAiT3Ntb2xhcmlkYWQiLAogICAgICAgeWxhYiA9ICJPeGFsYXRvIiwgY29sID0gY29sb3JlcykKY2RwbG90KGRhdG9zJHIgfiBkYXRvcyRjb25kLCB4bGFiID0gIkNvbmR1Y3RpdmlkYWQiLAogICAgICAgeWxhYiA9ICJPeGFsYXRvIiwgY29sID0gY29sb3JlcykKY2RwbG90KGRhdG9zJHIgfiBkYXRvcyR1cmVhLCB4bGFiID0gIlVyZWEiLAogICAgICAgeWxhYiA9ICJPeGFsYXRvIiwgY29sID0gY29sb3JlcykKY2RwbG90KGRhdG9zJHIgfiBkYXRvcyRjYWxjLCB4bGFiID0gIkNhbGNpbyIsCiAgICAgICB5bGFiID0gIk94YWxhdG8iLCBjb2wgPSBjb2xvcmVzKQpgYGAKCiMjIyBCb3hwbG90IGNvbXBhcmF0aXZvCgpgYGB7ciwgZmlnLmhlaWdodD01fQpkYXRvcyAlPiUgCiAgcmVuYW1lKE94YWxhdG8gPSByKSAlPiUgCiAgZ2F0aGVyKGtleSA9ICJ2YXJpYWJsZSIsIHZhbHVlID0gInZhbG9yIiwgLU94YWxhdG8pICU+JSAKICBnZ3Bsb3QoZGF0YSA9IC4sIGFlcyh4ID0gT3hhbGF0bywgeSA9IHZhbG9yLCBmaWxsID0gT3hhbGF0bykpICsKICBmYWNldF93cmFwKH52YXJpYWJsZSwgc2NhbGVzID0gImZyZWUiKSArCiAgZ2VvbV9ib3hwbG90KCkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGNvbG9yZXMpICsKICBsYWJzKHggPSAiIiwgeSA9ICIiKSArCiAgdGhlbWVfbGlnaHQoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpCmBgYAoKCiMgTW9kZWxvcwoKIyMjIEFqdXN0ZSBkZSBtb2RlbG9zCgpgYGB7ciwgZWNobz1UUlVFfQojIE1vZGVsb3MgTGluZWFsZXMgR2VuZXJhbGl6YWRvcwptb2RfbG9naXQgIDwtIGdsbShyIH4gLiwgZGF0YSA9IGRhdG9zLCBmYW1pbHkgPSBiaW5vbWlhbChsaW5rID0gImxvZ2l0IikpCm1vZF9wcm9iaXQgPC0gZ2xtKHIgfiAuLCBkYXRhID0gZGF0b3MsIGZhbWlseSA9IGJpbm9taWFsKGxpbmsgPSAicHJvYml0IikpCgojIC0tLS0tLS0tIFZhbGlkYWNpw7NuIExPT0NWIChNYW51YWwpCm91dF9sb2dpXzAuNSA8LSBOVUxMCm91dF9wcm9iXzAuNSA8LSBOVUxMCmZvcihpIGluIDE6bnJvdyhkYXRvcykpewogIG91dF9sb2dpXzAuNVtpXSA9IHByZWRpY3QodXBkYXRlKG1vZF9sb2dpdCwgZGF0YSA9IGRhdG9zWy1pLCBdKSwKICAgICAgICAgICAgICAgICAgIG5ld2RhdGEgPSBkYXRvc1tpLF0sIHR5cGUgPSAicmVzcG9uc2UiKQogIG91dF9wcm9iXzAuNVtpXSA9IHByZWRpY3QodXBkYXRlKG1vZF9wcm9iaXQsIGRhdGEgPSBkYXRvc1staSwgXSksCiAgICAgICAgICAgICAgICAgICBuZXdkYXRhID0gZGF0b3NbaSxdLCB0eXBlID0gInJlc3BvbnNlIikKfQoKIyAtLS0tLS0tLSBWYWxpZGFjacOzbiBMT09DViAoY3YuZ2xtKCkpCgojIyBGdW5jacOzbiBkZSBjb3N0ZSBjb24gY3V0b2ZmID0gMC41CmNvc3RlXzAuNSA8LSBmdW5jdGlvbihyLCBwaSA9IDApIG1lYW4oYWJzKHItcGkpPiAwLjUpCgojIyBMT09DVgpsaWJyYXJ5KGJvb3QpCmN2X2Vycm9yX2xvZ2lfMC41IDwtIGN2LmdsbShkYXRhID0gZGF0b3MgJT4lIGZpbHRlcighaXMubmEob3NtbykgJiAhaXMubmEoY29uZCkpLAogICAgICAgICAgICAgICAgICAgICAgICBnbG1maXQgPSBtb2RfbG9naXQsIGNvc3QgPSBjb3N0ZV8wLjUpCmN2X2Vycm9yX3Byb2JfMC41IDwtIGN2LmdsbShkYXRhID0gZGF0b3MgJT4lIGZpbHRlcighaXMubmEob3NtbykgJiAhaXMubmEoY29uZCkpLAogICAgICAgICAgICAgICAgICAgICAgICBnbG1maXQgPSBtb2RfcHJvYml0LCBjb3N0ID0gY29zdGVfMC41KQpgYGAKCi0gPHRyZWQ+KipPYnNlcnZhY2lvbmVzOioqPC90cmVkPiBsYSBlamVjdWNpw7NuIGF1dG9tw6F0aWNhIGRlICpMT09DViogY29uIGxhIGZ1bmNpw7NuIGBjdi5nbG0oKWAgcmVxdWllcmUgdW5hIGZ1bmNpw7NuIGRlIGNvc3RlIHBhcmEgY2FsY3VsYXIgZWwgZXJyb3IuIEVsIG9iamV0byBkZXZ1ZWx0byBwb3IgbGEgZnVuY2nDs24gZGVsIHBhcXVldGUgYGJvb3RgIGFsbWFjZW5hIGVsIGVycm9yIGNvbiBlbCBub21icmUgYGRlbHRhYDsgYWwgcmVzdGFyIDEgbWVub3MgZWwgZXJyb3IgKGRlbHRhKSBzZSBvYnRlbmRyw6EgbGEgcHJlY2lzacOzbiBvICphY2N1cmFjeSogZGVsIG1vZGVsbywgcXVlIGVzIGV4YWN0YW1lbnRlIGVsIG1pc21vIHZhbG9yIG9idGVuaWRvIG1hbnVhbG1lbnRlLgogICAgLSAqKkZ1bmNpw7NuIGRlIGNvc3RlOioqIGVuIGVsIGPDs2RpZ28gaGF5IHVuYSBmdW5jacOzbiBkZSBjb3N0ZSBvIHDDqXJkaWRhIHBhcmEgZWwgbMOtbWl0ZSBpZ3VhbCBhIDAuNS4gRXN0YSBmdW5jacOzbiBwdWVkZSBzZXIgZXhwcmVzYWRhIGRlIGxhIHNpZ3VpZW50ZSBtYW5lcmE6ICRlcnJvciA9IFxzdW0gfHJfaSAtIHBfaXwgPiAwLjUkLiBEb25kZSAkcl9pJCBlcyBlbCBpLcOpc2ltbyB2YWxvciByZWFsIHkgJHBfaSQgZXMgZWwgaS3DqXNpbW8gdmFsb3IgcHJlZGljaG8uIEVuIGVsIHNpZ3VpZW50ZSBjw7NkaWdvIGVzIHBvc2libGUgZXZpZGVuY2lhciBxdWUgc2Ugb2J0aWVuZW4gbG9zIG1pc21vcyByZXN1bHRhZG9zIGRlIGZvcm1hIG1hbnVhbCB5IGNvbiBsYSBmdW5jacOzbiBgY3YuZ2xtKClgLgogICAgCmBgYHtyLCBlY2hvPVRSVUV9CiMgUHJlY2lzacOzbiBtYW51YWwgLSBSLiBMb2fDrXN0aWNhICgwLjUpCm1hbnVhbF9sb2dpXzAuNSA8LSBpZl9lbHNlKG91dF9sb2dpXzAuNSA+IDAuNSwgdHJ1ZSA9ICIxIiwgZmFsc2UgPSAiMCIpCm1hbnVhbF9sb2dpXzAuNSA8LSBtZWFuKGRhdG9zJHIgPT0gbWFudWFsX2xvZ2lfMC41LCBuYS5ybSA9IFRSVUUpCgojIFByZWNpc2nDs24gY29uIGN2LmdsbSgpIC0gUi4gTG9nw61zdGljYSAoMC41KQpjdl9sb2dpXzAuNSA8LSAxIC0gY3ZfZXJyb3JfbG9naV8wLjUkZGVsdGFbMV0KCiMgUHJlY2lzacOzbiBtYW51YWwgLSBSLiBQcm9iaXQgKDAuNSkKbWFudWFsX3Byb2JfMC41IDwtIGlmX2Vsc2Uob3V0X3Byb2JfMC41ID4gMC41LCB0cnVlID0gIjEiLCBmYWxzZSA9ICIwIikKbWFudWFsX3Byb2JfMC41IDwtIG1lYW4oZGF0b3MkciA9PSBtYW51YWxfcHJvYl8wLjUsIG5hLnJtID0gVFJVRSkKCiMgUHJlY2lzacOzbiBjb24gY3YuZ2xtKCkgLSBSLiBMb2fDrXN0aWNhICgwLjUpCmN2X3Byb2JfMC41IDwtIDEgLSBjdl9lcnJvcl9wcm9iXzAuNSRkZWx0YVsxXQoKIyBJbXByaW1pZW5kbyByZXN1bHRhZG9zCmNhdChwYXN0ZTAoIlIuIExvZ8Otc3RpY2EgMC41IE1hbnVhbCA9ICIsIG1hbnVhbF9sb2dpXzAuNSksCiAgICAieSBSLiBMb2fDrXN0aWNhIDAuNSBjdi5nbG0oKSA9ICIsIGN2X2xvZ2lfMC41KQpgYGAKICAgIAoKIyMjIFJlc3VtZW4gUi4gTG9nw61zdGljYQoKYGBge3IsIGVjaG89VFJVRX0KdGlkeShtb2RfbG9naXQpICU+JSAKICBzZWxlY3QodGVybSwgZXN0aW1hdGUsIHAudmFsdWUpICU+JSAKICBtdXRhdGUoc2lnbmlmID0gaWZfZWxzZShwLnZhbHVlIDw9IDAuMDUsIHRydWUgPSAiU2lnbmlmaWNhdGl2byIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgZmFsc2UgPSAiTm8gc2lnbmlmaWNhdGl2byIpKQpgYGAKCgojIyMgUmVzdW1lbiBSLiBQcm9iaXQKCmBgYHtyLCBlY2hvPVRSVUV9CnRpZHkobW9kX3Byb2JpdCkgJT4lIAogIHNlbGVjdCh0ZXJtLCBlc3RpbWF0ZSwgcC52YWx1ZSkgJT4lIAogIG11dGF0ZShzaWduaWYgPSBpZl9lbHNlKHAudmFsdWUgPD0gMC4wNSwgdHJ1ZSA9ICJTaWduaWZpY2F0aXZvIiwKICAgICAgICAgICAgICAgICAgICAgICAgICBmYWxzZSA9ICJObyBzaWduaWZpY2F0aXZvIikpCmBgYAoKIyBDb21wYXJhY2nDs24gZGUgTW9kZWxvcwoKLSAqKlRhYmxhOioqCgpgYGB7ciwgZWNobyA9IFRSVUV9CiMgUHVudG8gZGUgY29ydGUgMC42NSAtIGNsYXNlcyAwIHkgMQpvdXRfbG9naV8wLjY1IDwtIGlmX2Vsc2UoY29uZGl0aW9uID0gb3V0X2xvZ2kgPiAwLjY1LCB0cnVlID0gIjEiLCBmYWxzZSA9ICIwIikKb3V0X3Byb2JfMC42NSA8LSBpZl9lbHNlKGNvbmRpdGlvbiA9IG91dF9wcm9iID4gMC42NSwgdHJ1ZSA9ICIxIiwgZmFsc2UgPSAiMCIpCgojIFB1bnRvIGRlIGNvcnRlIDAuNiAtIGNsYXNlcyAwIHkgMQpvdXRfbG9naV8wLjYgPC0gaWZfZWxzZShjb25kaXRpb24gPSBvdXRfbG9naSA+IDAuNiwgdHJ1ZSA9ICIxIiwgZmFsc2UgPSAiMCIpCm91dF9wcm9iXzAuNiA8LSBpZl9lbHNlKGNvbmRpdGlvbiA9IG91dF9wcm9iID4gMC42LCB0cnVlID0gIjEiLCBmYWxzZSA9ICIwIikKCiMgUHVudG8gZGUgY29ydGUgMC41NSAtIGNsYXNlcyAwIHkgMQpvdXRfbG9naV8wLjU1IDwtIGlmX2Vsc2UoY29uZGl0aW9uID0gb3V0X2xvZ2kgPiAwLjU1LCB0cnVlID0gIjEiLCBmYWxzZSA9ICIwIikKb3V0X3Byb2JfMC41NSA8LSBpZl9lbHNlKGNvbmRpdGlvbiA9IG91dF9wcm9iID4gMC41NSwgdHJ1ZSA9ICIxIiwgZmFsc2UgPSAiMCIpCgojIFB1bnRvIGRlIGNvcnRlIDAuNSAtIGNsYXNlcyAwIHkgMQpvdXRfbG9naV8wLjUgPC0gaWZfZWxzZShjb25kaXRpb24gPSBvdXRfbG9naSA+IDAuNSwgdHJ1ZSA9ICIxIiwgZmFsc2UgPSAiMCIpCm91dF9wcm9iXzAuNSA8LSBpZl9lbHNlKGNvbmRpdGlvbiA9IG91dF9wcm9iID4gMC41LCB0cnVlID0gIjEiLCBmYWxzZSA9ICIwIikKCiMgUHVudG8gZGUgY29ydGUgMC40NSAtIGNsYXNlcyAwIHkgMQpvdXRfbG9naV8wLjQ1IDwtIGlmX2Vsc2UoY29uZGl0aW9uID0gb3V0X2xvZ2kgPiAwLjQ1LCB0cnVlID0gIjEiLCBmYWxzZSA9ICIwIikKb3V0X3Byb2JfMC40NSA8LSBpZl9lbHNlKGNvbmRpdGlvbiA9IG91dF9wcm9iID4gMC40NSwgdHJ1ZSA9ICIxIiwgZmFsc2UgPSAiMCIpCgojIFB1bnRvIGRlIGNvcnRlIDAuNCAtIGNsYXNlcyAwIHkgMQpvdXRfbG9naV8wLjQgPC0gaWZfZWxzZShjb25kaXRpb24gPSBvdXRfbG9naSA+IDAuNCwgdHJ1ZSA9ICIxIiwgZmFsc2UgPSAiMCIpCm91dF9wcm9iXzAuNCA8LSBpZl9lbHNlKGNvbmRpdGlvbiA9IG91dF9wcm9iID4gMC40LCB0cnVlID0gIjEiLCBmYWxzZSA9ICIwIikKCiMgRXJyb3IgZGUgbW9kZWxvcwpkZl9hY2N1cmFjeSA8LSBkYXRhLmZyYW1lKAogIE1vZGVsbyA9IGMoIlIuIExvZ8Otc3RpY2EgLSAoMC42NSkiLCAiUi4gUHJvYml0IC0gKDAuNjUpIiwKICAgICAgICAgICAgICJSLiBMb2fDrXN0aWNhIC0gKDAuNikiLCAiUi4gUHJvYml0IC0gKDAuNikiLAogICAgICAgICAgICAgIlIuIExvZ8Otc3RpY2EgLSAoMC41NSkiLCAiUi4gUHJvYml0IC0gKDAuNTUpIiwKICAgICAgICAgICAgICJSLiBMb2fDrXN0aWNhIC0gKDAuNSkiLCAiUi4gUHJvYml0IC0gKDAuNSkiLAogICAgICAgICAgICAgIlIuIExvZ8Otc3RpY2EgLSAoMC40NSkiLCAiUi4gUHJvYml0IC0gKDAuNDUpIiwKICAgICAgICAgICAgICJSLiBMb2fDrXN0aWNhIC0gKDAuNCkiLCAiUi4gUHJvYml0IC0gKDAuNCkiKSwKICBBY2N1Y2FyeSA9IGMobWVhbihkYXRvcyRyID09IG91dF9sb2dpXzAuNjUsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgICAgIG1lYW4oZGF0b3MkciA9PSBvdXRfcHJvYl8wLjY1LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICAgICBtZWFuKGRhdG9zJHIgPT0gb3V0X2xvZ2lfMC42LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICAgICBtZWFuKGRhdG9zJHIgPT0gb3V0X3Byb2JfMC42LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICAgICBtZWFuKGRhdG9zJHIgPT0gb3V0X2xvZ2lfMC41NSwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgICAgbWVhbihkYXRvcyRyID09IG91dF9wcm9iXzAuNTUsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgICAgIG1lYW4oZGF0b3MkciA9PSBvdXRfbG9naV8wLjUsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgICAgIG1lYW4oZGF0b3MkciA9PSBvdXRfcHJvYl8wLjUsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgICAgIG1lYW4oZGF0b3MkciA9PSBvdXRfbG9naV8wLjQ1LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICAgICBtZWFuKGRhdG9zJHIgPT0gb3V0X3Byb2JfMC40NSwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgICAgbWVhbihkYXRvcyRyID09IG91dF9sb2dpXzAuNCwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgICAgbWVhbihkYXRvcyRyID09IG91dF9wcm9iXzAuNCwgbmEucm0gPSBUUlVFKSkKKSAlPiUgCiAgc2VwYXJhdGUoTW9kZWxvLCBpbnRvID0gYygiTW9kZWxvIiwgIkzDrW1pdGUiKSwgcmVtb3ZlID0gRkFMU0UsIHNlcCA9ICItIikKCmRmX2FjY3VyYWN5CmBgYAoKLSAqKkdyw6FmaWNvOioqCgpgYGB7cn0KZGZfYWNjdXJhY3kgJT4lIAogIGdncGxvdChkYXRhID0gLiwgYWVzKHggPSBMw61taXRlLCB5ID0gQWNjdWNhcnksIGNvbG9yID0gTW9kZWxvKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDQsIHBjaCA9IDE4KSArCiAgZ2VvbV9saW5lKGFlcyhncm91cCA9IE1vZGVsbyksIGx3ZCA9IDAuOCkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjb2xvcmVzKSArCiAgbGFicyh4ID0gIkzDrW1pdGUgKHRocmVzaG9sZCkiLCB5ID0gIkFjY3VyYWN5IiwKICAgICAgIGNvbG9yID0gIiIsIHRpdGxlID0gIkFjY3VyYWN5IGNvbiBkaWZlcmVudGVzIGzDrW1pdGVzIGRlIHByb2JhYmlsaWRhZCIpICsKICB0aGVtZV9saWdodCgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikKICAKYGBgCgoKIyBNb2RlbG8gRmluYWwKCiMjIyBDb25jbHVzaW9uZXMKCi0gQW1ib3MgbW9kZWxvcyBwcmVzZW50YW4gZGVzZW1wZcOxb3Mgc2ltaWxhcmVzIGVuIGxhIGRldGVjY2nDs24gZGUgb3hhbGF0b3MgZW4gb3JpbmEuICBQYXJhIGVsIG1vZGVsbyBsb2fDrXN0aWNvIHNvbiBlc3RhZMOtc3RpY2FtZW50ZSBzaWduaWZpY2F0aXZhcyBsYXMgdmFyaWFibGVzIHVyZWEgKCRwPTAuMDQ3MDI1NDY5JCkgeSBjYWxjaW8gKCRwPTAuMDAxMjExMzEyJCksIG5vIG9ic3RhbnRlLCBwYXJhIGVsIG1vZGVsbyBwcm9iaXQgc8OzbG8gZXMgc2lnbmlmaWNhdGl2YSBsYSB2YXJpYWJsZSBjYWxjaW8gKCRwID0gMC4wMDA0Mzg2MjQ1JCkuCi0gRWwgc2lnbm8gZGUgbG9zIGNvZWZpY2llbnRlcyBlc3RpbWFkb3MgcGFyYSBhbWJvcyBtb2RlbG9zIGVzIGVsIG1pc21vLiBFc3RvIGluZGljYSBxdWUgYW1iYXMgYXByb3hpbWFjaW9uZXMgcG9zZWVuIHNpbWlsaXR1ZCBlbiBjdWFudG8gYSBsYSByZWxhY2nDs24gZGUgY2FtYmlvLCBzaW4gZW1iYXJnbywgbGEgbWFnbml0dWQgZXN0aW1hZGEgZXMgZGlmZXJlbnRlLgotIFNlIG9ic2VydmEgZGlmZXJlbmNpYSBlbiBsYSBwcmVjaXNpw7NuIGRlIGxvcyBtb2RlbG9zIHBhcmEgcHVudG9zIGRlIGNvcnRlICgqY3V0b2ZmKikgZGlzdGludG9zLCBvYnRlbmllbmRvIGxhcyBtZWpvcmVzIHByZWRpY2Npb25lcyBjb24gbMOtbWl0ZXMgaWd1YWxlcyBhIDAuNiB5IDAuNjUuIExhIHByZWNpc2nDs24gZXMgbcOhcyBiYWphIGNvbiBhbWJvcyBtb2RlbG9zIGN1YW5kbyBzZSBlbXBsZWFuIHVtYnJhbGVzIGlndWFsZXMgYSAwLjQ1IHkgMC41CgojIyMgTW9kZWxvIExvZ8Otc3RpY28KCi0gRW4gdMOpcm1pbm9zIGxvZ2Fyw610bWljb3MgZWwgbW9kZWxvIHB1ZWRlIHNlciBleHByZXNhZG8gZGUgbGEgc2lndWllbnRlIG1hbmVyYToKCiQkbG9naXQob3hhbGF0byA9IDEpID0gLTM1NS4zMzc3ICsgMzU1Ljk0MzdcdGltZXMgZ3JhdiAtIDAuNDk1N1x0aW1lcyBwSCArXFwgMC4wMTY4XHRpbWVzIG9zbW8gLSAwLjQzMjhcdGltZXMgY29uZHVjIC0gMC4wMzIwXHRpbWVzIHVyZWEgKyAwLjc4MzYgXHRpbWVzIGNhbGNpbyQkCgotIFNpIHNlIHF1aWVyZSBjYWxjdWxhciBsYSBwcm9iYWJpbGlkYWQgcGFyYSBjdWFscXVpZXIgY29tYmluYWNpw7NuIGRlIHZhbG9yZXMgZGUgcHJlZGljdG9yYXMsIHNlIHB1ZWRlIG9idGVuZXIgZGUgbGEgc2lndWllbnRlIG1hbmVyYToKCiQkcChveGFsYXRvID0gMSkgPSBcZnJhY3tlXnstMzU1LjMzNzcgKyAzNTUuOTQzN1x0aW1lcyBncmF2IC0gMC40OTU3XHRpbWVzIHBIICsgMC4wMTY4XHRpbWVzIG9zbW8gLSAwLjQzMjhcdGltZXMgY29uZHVjIC0gMC4wMzIwXHRpbWVzIHVyZWEgKyAwLjc4MzYgXHRpbWVzIGNhbGNpb319ezEgKyBlXnstMzU1LjMzNzcgKyAzNTUuOTQzN1x0aW1lcyBncmF2IC0gMC40OTU3XHRpbWVzIHBIICsgMC4wMTY4XHRpbWVzIG9zbW8gLSAwLjQzMjhcdGltZXMgY29uZHVjIC0gMC4wMzIwXHRpbWVzIHVyZWEgKyAwLjc4MzYgXHRpbWVzIGNhbGNpb319JCQKCiMgUmVmZXJlbmNpYXMKCjEuIFtMaWJybzogQW4gSW50cm9kdWN0aW9uIHRvIFN0YXRpc3RpY2FsIExlYXJuaW5nIChjaGFwdGVyIDQgLSBDbGFzc2lmaWNhdGlvbildKGh0dHA6Ly93d3cuaW1lLnVuaWNhbXAuYnIvfmRpYXMvSW50b2R1Y3Rpb24lMjB0byUyMFN0YXRpc3RpY2FsJTIwTGVhcm5pbmcucGRmKQoyLiBbTGlicm86IFRoZSBFbGVtZW50cyBvZiBTdGF0aXN0aWNhbCBMZWFybmluZyAoY2hhcHRlciA0IC0gQ2xhc3NpZmljYXRpb24pXShodHRwczovL3dlYi5zdGFuZm9yZC5lZHUvfmhhc3RpZS9QYXBlcnMvRVNMSUkucGRmKQozLiBbQWR2YW5jZWQgUmVncmVzc2lvbiBNb2RlbHNdKGh0dHA6Ly9yLXN0YXRpc3RpY3MuY28vYWR2LXJlZ3Jlc3Npb24tbW9kZWxzLmh0bWwpCjQuIFtMb2dpc3RpYyBSZWdyZXNzaW9uXShodHRwOi8vci1zdGF0aXN0aWNzLmNvL0xvZ2lzdGljLVJlZ3Jlc3Npb24tV2l0aC1SLmh0bWwpCjUuIFtQcm9iaXQgUmVncmVzc2lvbl0oaHR0cDovL3Itc3RhdGlzdGljcy5jby9Qcm9iaXQtUmVncmVzc2lvbi1XaXRoLVIuaHRtbCk=