Introduccion
La inferencia causal es una metodología estadística y analítica que
busca identificar relaciones de causa y efecto entre variables, más allá
de simples correlaciones. Su objetivo es responder preguntas del tipo
“¿qué pasaría si…?”, permitiendo estimar el impacto de una intervención
o cambio en una variable sobre otra. Esta técnica es ampliamente
utilizada en campos como la economía, la medicina, las ciencias
sociales, el marketing y el aprendizaje automático, donde es crucial
entender cómo ciertas acciones o políticas influyen en los resultados
observados. Por ejemplo, puede emplearse para evaluar el efecto de una
política pública, el impacto de un tratamiento médico o el rendimiento
de una estrategia comercial.
En este caso, asumiremos que a unos investigadores les interesa saber
si el uso de mosquiteros disminuye el riesgo de que una persona
contraiga malaria. Para ello han recopilado datos de 1.752 hogares en un
país sin nombre y tienen variables relacionadas con los factores
ambientales, salud individual y las características de los hogares.
Además, este país tiene un programa especial que proporciona mosquiteros
gratuitos a los hogares que cumplen con requisitos específicos: para
calificar para el programa, debe haber más de 4 miembros del hogar, y el
ingreso mensual del hogar debe ser inferior a 700 dólares al mes. Los
hogares no se inscriben automáticamente en el programa, y muchos no lo
usan. Los datos no son experimentales - los investigadores no tienen
control sobre quién usa los mosquiteros, y los hogares individuales
toman sus propias decisiones sobre si solicitan mosquiteros gratuitos o
compran sus propios mosquiteros, así como si usan los mosquiteros si los
tienen.
Importamos
librerias
library(dagitty) # Para crear, analizar y evaluar diagramas causales (DAGs)
library(ggdag) # Extensión de ggplot2 para visualizar y personalizar DAGs
library(tidyverse) # Conjunto de herramientas para manipulación, transformación y análisis de datos
library(ggplot2) # Para crear gráficos elegantes y personalizables en R
library(MatchIt) # Para realizar emparejamiento de muestras (matching) en análisis causal
library(XReg) # Salida de modelos de regresion
library(texreg) # Resumen y comparaciones de modelos
Modelamos DAG (Diagram
Aclyclic Graph)
mosquito_dag <- dagify(
riesgo_malaria ~ red + ingreso + salud + temperatura + resistencia,
red ~ ingreso + salud + temperatura + elegible + hogar,
elegible ~ ingreso + hogar,
salud ~ ingreso,
exposure = "red",
outcome = "riesgo_malaria",
coords = list(x = c(riesgo_malaria = 7, red = 3, ingreso = 4, salud = 5,
temperatura = 6, resistencia = 8.5, elegible = 2, hogar = 1),
y = c(riesgo_malaria = 2, red = 2, ingreso = 3, salud = 1,
temperatura = 3, resistencia = 2, elegible = 3, hogar = 2)),
labels = c(riesgo_malaria = "Riesgo de malaria", red = "Mosquitero", ingreso = "Ingreso",
salud = "Salud", temperatura = "temperaturas nocturnas",
resistencia = "Resistencia a insecticidas",
elegible = "Eligible para el programa", hogar = "Miembros en el hogar")
)
ggdag_status(mosquito_dag, use_labels = "label", text = FALSE) +
theme_dag()

Este código en R construye y visualiza un grafo acíclico dirigido
(DAG, por sus siglas en inglés) que representa relaciones causales
hipotéticas entre variables relacionadas con el riesgo de malaria.
Utiliza la función dagify() para definir cómo se cree que las variables
están causalmente conectadas: por ejemplo, el uso de mosquiteros (red)
depende de factores como ingreso, salud, temperatura, elegibilidad y
tamaño del hogar, y a su vez influye en el riesgo de malaria
(riesgo_malaria). Se especifican las variables de exposición (red) y
resultado (riesgo_malaria), así como las coordenadas para su
visualización y etiquetas descriptivas. Finalmente, ggdag_status()
genera un gráfico que muestra el estado de las variables en el DAG, útil
para analizar supuestos de identificación causal y planear estrategias
de estimación.
Cada nodo en el DAG es una columna en la base de datos recogida por
los investigadores, e incluye lo siguiente:
Riesgo de malaria (malaria_risk): La probabilidad de
que alguien en el hogar se infecte con malaria. Se mide en una escala de
0-100, con valores más altos que indican un mayor riesgo.
Mosquitero ((net and net_num): Una variable binaria
que indica si el hogar utiliza mosquiteros.
Elegible para el programa (eligible): Una variable
binaria que indica si el hogar es elegible para el programa de
mosquiteros gratuitos.
Ingresos (income): Los ingresos mensuales del hogar,
en dólares americanos.
Temperaturas nocturnas (temperature): La temperatura
media nocturna, en grados centígrados.
Salud (health): La salud de la familia se declarada
por sus propios miembros. Medido en una escala de 0-100, con valores más
altos que indican mejor salud.
Número en el hogar (household): Número de personas
que viven en el hogar.
Resistencia a los insecticidas (resistance): Algunas
cepas de mosquitos son más resistentes a los insecticidas y, por lo
tanto, suponen un mayor riesgo de infectar a las personas con malaria.
Esto se mide en una escala de 0-100, con valores más altos que indican
una mayor resistencia.
Según el DAG, el riesgo de contraer malaria es causado por los
ingresos, las temperaturas, la salud, la resistencia a los insecticidas
y el uso de mosquiteros. Las personas que viven en regiones más cálidas,
tienen ingresos más bajos, tienen peor salud, están rodeadas de
mosquitos con alta resistencia a los insecticidas y los que no utilizan
mosquiteros corren un riesgo mayor de contraer malaria que los que los
utilizan. El uso de los mosquiteros se explica por los ingresos, las
temperaturas nocturnas, la salud, el número de personas que viven en la
vivienda y la elegibilidad para el programa de mosquiteros gratuitos.
Las personas que viven en zonas más frescas por la noche, tienen
ingresos más altos, mejor salud, tienen más personas en el hogar, y son
elegibles para los mosquiteros gratuitos del gobierno, tienen más
probabilidades de usar regularmente los mosquiteros. El DAG también
muestra que la elegibilidad para el programa de mosquiteros gratuitos se
explica por los ingresos y el tamaño de los núcleos familiares, ya que
los hogares deben cumplir con umbrales específicos para calificar.
Cargamos la base de
datos
load("~/Library/Mobile Documents/com~apple~CloudDocs/Cursos/Causal Inference/Causal Inference in R/R for political data science/data/mosquito_nets.rda")
glimpse(mosquito_nets)
Rows: 1,752
Columns: 10
$ id <dbl> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28,…
$ net <lgl> TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, TRUE, TRUE, FALSE, FALSE, TR…
$ net_num <dbl> 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, …
$ malaria_risk <dbl> 33, 42, 80, 34, 44, 25, 19, 35, 32, 40, 30, 14, 31, 34, 22, 54, 23, 41, 37, 65, 59, 59, 41, 21, 25, 27…
$ income <dbl> 781, 974, 502, 671, 728, 1050, 1146, 1093, 1037, 828, 889, 1133, 807, 950, 1195, 594, 936, 816, 962, 4…
$ health <dbl> 56, 57, 15, 20, 17, 48, 65, 75, 60, 36, 75, 62, 42, 53, 90, 29, 47, 30, 55, 6, 36, 30, 45, 64, 62, 75,…
$ household <dbl> 2, 4, 3, 5, 5, 1, 3, 5, 3, 3, 6, 3, 4, 3, 1, 5, 2, 1, 2, 6, 5, 4, 3, 7, 3, 4, 3, 3, 2, 2, 6, 5, 4, 4, …
$ eligible <lgl> FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE…
$ temperature <dbl> 21.1, 26.5, 25.6, 21.3, 19.2, 25.3, 27.4, 29.8, 27.6, 21.3, 17.4, 18.9, 27.6, 24.0, 24.5, 25.5, 26.1, …
$ resistance <dbl> 59, 73, 65, 46, 54, 34, 45, 65, 55, 54, 33, 39, 37, 53, 34, 55, 32, 73, 54, 38, 51, 43, 53, 51, 33, 37…
Analisamos las
independencias condicionales
El comando impliedConditionalIndependencies()
se utiliza
en el paquete dagitty en R para analizar un DAG (Gráfico Acíclico
Dirigido).Este comando identifica las independencias condicionales que
deberían existir en el modelo, basándose en su estructura. Teniendo en
cuenta que el signo se interpreta como independencia ⊥
.
Cuando dos variables son independientes incondicionalmente, significa
que no tienen relación directa o indirecta, y conocer el valor de una no
proporciona información sobre la otra. Por ejemplo, si A y B son
incondicionalmente independientes, el conocimiento de A no afecta
nuestra predicción sobre B. Por otro lado, dos variables son
independientes condicionalmente si dejan de estar relacionadas al
conocer el valor de una tercera variable C. En este caso, A y B pueden
estar correlacionadas inicialmente, pero esa correlación desaparece al
controlar por C.
Primero, verificar independecias condicionales
impliedConditionalIndependencies(mosquito_dag)
elgb _||_ rsst
elgb _||_ rsg_ | ingr, red, sald, tmpr
elgb _||_ sald | ingr
elgb _||_ tmpr
hogr _||_ ingr
hogr _||_ rsst
hogr _||_ rsg_ | ingr, red, sald, tmpr
hogr _||_ sald
hogr _||_ tmpr
ingr _||_ rsst
ingr _||_ tmpr
red _||_ rsst
rsst _||_ sald
rsst _||_ tmpr
sald _||_ tmpr
las independencias incondicionales (como elgb || rsst)
significan que, basándonos en la estructura del DAG, las variables
“Eligible para el programa” (elegible) y “Resistencia a insecticidas”
(resistencia) no tienen una relación directa o indirecta; conocer el
estado de una no cambia nada sobre la otra. Por otro lado, las
independencias condicionales (como elgb || sald | ingr) indican
que, aunque podría haber una relación entre “Elegible” y “Salud”, esta
relación desaparece si conocemos “Ingreso”, que actúa como variable de
ajuste.
Segundo, validamos correlación entre las
variables
# Elegible ⊥ resistencia
cor(mosquito_nets$eligible,mosquito_nets$resistance)
[1] 0.01234798
# Elegible ⊥ temperatura
cor(mosquito_nets$eligible,mosquito_nets$temperature)
[1] -0.02092867
# Miembros Hogar ⊥ Ingreso
cor(mosquito_nets$household,mosquito_nets$income)
[1] 0.008606828
# Miembros Hogar ⊥ Resistencia
cor(mosquito_nets$household,mosquito_nets$resistance)
[1] 0.02036479
# Miembros Hogar ⊥ Salud
cor(mosquito_nets$household,mosquito_nets$health)
[1] 0.00009785337
# Miembros Hogar ⊥ temperatura
cor(mosquito_nets$household,mosquito_nets$temperature)
[1] -0.02355615
# Ingreso ⊥ resistencias
cor(mosquito_nets$income,mosquito_nets$resistance)
[1] 0.01371297
# Ingreso ⊥ temperatura
cor(mosquito_nets$income,mosquito_nets$temperature)
[1] 0.1145676
# Red ⊥ resistencia
cor(mosquito_nets$net_num,mosquito_nets$resistance)
[1] 0.01677202
# resistencia ⊥ salud
cor(mosquito_nets$resistance,mosquito_nets$health)
[1] 0.007841029
# resistencia ⊥ temperatura
cor(mosquito_nets$resistance,mosquito_nets$temperature)
[1] -0.02198524
# salud ⊥ temperatura
cor(mosquito_nets$health,mosquito_nets$temperature)
[1] 0.1199855
Podemos que observar que la correlación entre las variables
independientes incondicionadas es significativamente bajas. Sugiriendo
la ausencia de dependencias entre ellas
Evaluamos la
independencia condicional
El riesgo de malaria debe ser independiente del número de miembros de
la familia, dado que los niveles de salud, ingresos, uso de mosquiteros
y temperaturas nocturnas son similares.
No podemos utilizar cor() para probar esta implicación, ya que hay
muchas variables en juego, pero podemos utilizar un modelo de regresión
para comprobar si el número de miembros del hogar está
significativamente relacionado con el riesgo de malaria
# Riesgo de malaria ⊥ Miembros en el hogar| Salud, Ingreso, Uso de la red, Temperatura:
lm(malaria_risk ~ household + health + income + net_num + temperature,
data = mosquito_nets) %>%
summary()
Call:
lm(formula = malaria_risk ~ household + health + income + net_num +
temperature, data = mosquito_nets)
Residuals:
Min 1Q Median 3Q Max
-13.1458 -3.9117 -0.5715 3.3512 16.4438
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 76.206730 0.965788 78.906 <0.0000000000000002 ***
household -0.015453 0.089311 -0.173 0.863
health 0.148337 0.010672 13.900 <0.0000000000000002 ***
income -0.075144 0.001036 -72.563 <0.0000000000000002 ***
net_num -10.437014 0.266500 -39.163 <0.0000000000000002 ***
temperature 1.005790 0.030964 32.483 <0.0000000000000002 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 5.245 on 1746 degrees of freedom
Multiple R-squared: 0.8852, Adjusted R-squared: 0.8848
F-statistic: 2692 on 5 and 1746 DF, p-value: < 0.00000000000000022
No es significativo (t= −0.17, p = 0.863), lo que significa que los
dos son independientes, como se esperaba. Por lo tanto, la variable
riesgos de malaria es independiente a la variable miembros del
hogar.
Si hay correlaciones sustanciales y significativas entre los nodos
que deberían ser independientes, es probable que haya un problema con la
especificación del DAG.
Para este ejemplo en concreto hemos validado que no hay dependencia
significativas y que nuestro DAG calcula la realizad del modelo.
Econtramos el set de
ajuste
Existe una vía directa entre el uso de mosquiteros y el riesgo de
contraer malaria, pero el efecto no se identifica causalmente debido a
otras varias vías abiertas. Podemos dejar que R encuentre los sets de
ajuste apropiados automáticamente:
# Comando para evualuar los caminos del DAG
paths(mosquito_dag)
$paths
[1] "red -> riesgo_malaria"
[2] "red <- elegible <- ingreso -> riesgo_malaria"
[3] "red <- elegible <- ingreso -> salud -> riesgo_malaria"
[4] "red <- hogar -> elegible <- ingreso -> riesgo_malaria"
[5] "red <- hogar -> elegible <- ingreso -> salud -> riesgo_malaria"
[6] "red <- ingreso -> riesgo_malaria"
[7] "red <- ingreso -> salud -> riesgo_malaria"
[8] "red <- salud -> riesgo_malaria"
[9] "red <- salud <- ingreso -> riesgo_malaria"
[10] "red <- temperatura -> riesgo_malaria"
$open
[1] TRUE TRUE TRUE FALSE FALSE TRUE TRUE TRUE TRUE TRUE
$paths
Muestra todas las rutas (causales, indirectas, espurias) entre
variables en el DAG. Esto es útil para visualizar cómo las variables
están conectadas directa e indirectamente.
$open
La lista $open indica si las rutas están abiertas o cerradas. Las
rutas [4], [5] están cerradas (FALSE), lo que sugiere que están
bloqueadas por el control de ciertas variables. Para entender, la ruta 4
está cerrada por la variable elegibles, ya que no permite el flujo de
ingreso (variable confusa) a la variable tratamiento a pesar de que si
tiene influencia en el outcome.
Una ruta se determina abierta o cerrada dependiendo si permite el
flujo causal. Es decir, una ruta es abierta (TRUE) si hay relación entre
la exposición (tratamiento) y el resultado (outcome), ya sea por medio
de una variable confusor o de forma directa como la ruta 1.
El siguente comando identifica las variables que deben ajustarse para
obtener una estimación no sesgada del efecto causal. Las variables de
ajuste son aquellas que “cierran” las rutas de confusión, permitiendo
analizar el efecto directo entre las variables de interés.
El efecto de confusión ocurre cuando la relación observada entre dos
variables está influenciada por una tercera variable (llamada confusor)
que afecta tanto a la variable independiente (exposición) como a la
variable dependiente (resultado). Este efecto puede distorsionar o
sesgar la estimación del efecto causal, llevando a conclusiones
incorrectas.
# Variables necesarias para controlar efecto de confusion
adjustmentSets(mosquito_dag)
{ ingreso, salud, temperatura }
Basándose en las relaciones entre todos los nodos del DAG, el ajuste
por salud, ingresos y temperatura es suficiente para cerrar todas las
puertas traseras e identificar la relación entre el uso de la red y el
riesgo de malaria. Es importante destacar que no tenemos que
preocuparnos por ninguno de los nodos relacionados con el programa
gubernamental de redes gratuitas, ya que esos nodos no están d
conectados con el riesgo de malaria. Sólo tenemos que preocuparnos por
las relaciones de confusión.
Esto significa que, para estimar correctamente el efecto causal de la
exposición (por ejemplo, red) sobre el resultado (por ejemplo,
riesgo_malaria), es necesario controlar (o ajustar) por las variables
ingreso, salud, y temperatura, ya que estas variables influyen tanto en
el tratamiento (red) como en el resultado (malaria), bajo el termino en
ingles confounder
.
Podemos confirmar esto gráficamente con ggdag_adjustment_set():
ggdag_adjustment_set(mosquito_dag, shadow = TRUE,
use_labels = "label", text = FALSE)

Estimación ingenua no
ajustada
Primero, podemos ver cuál es la relación entre el
uso de mosquiteros y el riesgo de malaria en ausencia de cualquier
ajuste. Si creamos un cuadro de la distribución del riesgo de malaria
entre las personas que usan y no usan mosquiteros, vemos que el riesgo
medio es sustancialmente menor entre los que usan mosquiteros
ggplot(mosquito_nets, aes(x = net, y = malaria_risk)) +
geom_boxplot()

Podemos ejecutar un simple modelo de regresión para medir la
diferencia promedio exacta:
model_naive <- lm(malaria_risk ~ net, data = mosquito_nets)
tbl_regression(model_naive)
G1;H1;Errorh in tbl_regression(model_naive) :
could not find function "tbl_regression"
Error during wrapup: not that many frames on the stack
Error: no more error handlers available (recursive errors?); invoking 'abort' restart
g
Call:
lm(formula = malaria_risk ~ net, data = mosquito_nets)
Residuals:
Min 1Q Median 3Q Max
-26.937 -9.605 -1.937 7.063 55.395
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 41.9365 0.4049 103.57 <0.0000000000000002 ***
netTRUE -16.3315 0.6495 -25.15 <0.0000000000000002 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 13.25 on 1750 degrees of freedom
Multiple R-squared: 0.2654, Adjusted R-squared: 0.265
F-statistic: 632.3 on 1 and 1750 DF, p-value: < 0.00000000000000022
Según este modelo, parece que el uso de un mosquitero está asociado
con una disminución de 16 puntos en el riesgo de malaria. Sin embargo,
este no es el efecto causal. Este es un caso en el que la correlación no
es igual a la causalidad. Otros factores como los ingresos, la salud y
las temperaturas confunden la relación entre el uso de los mosquiteros y
el riesgo
Regresiones
Una forma rápida y fácil de tratar de ajustar las variables de
confusión es incluirlas como covariables en una regresión lineal. A
primera vista, esto tiene sentido intuitivo: el objetivo del ajuste es
comparar los nodos de tratamiento y de resultado con los mismos valores
de los diversos confusores, y el propósito de la regresión múltiple es
explicar la variación del resultado manteniendo constantes las
diferentes variables explicativas. Sin embargo, hacer ajustes de
confusión con la regresión lineal dará como resultado relaciones
causales correctamente identificadas sólo en circunstancias muy
específicas. Para que el ajuste basado en la regresión funcione, las
relaciones entre todos los nodos de tratamiento, resultado y confusión
deben ser lineales, lo cual es difícil de probar y verificar con datos
de observación reales. Casi siempre es mejor utilizar una de las otras
técnicas de ajuste que se describen a continuación (matching o
ponderación de probabilidad inversa), ya que esos métodos no se basan en
el supuesto de la linealidad.
model_regression <- lm(malaria_risk ~ net + income + temperature + health,
data = mosquito_nets)
summary(model_regression)
Call:
lm(formula = malaria_risk ~ net + income + temperature + health,
data = mosquito_nets)
Residuals:
Min 1Q Median 3Q Max
-13.143 -3.915 -0.561 3.333 16.461
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 76.159901 0.926835 82.17 <0.0000000000000002 ***
netTRUE -10.441932 0.264906 -39.42 <0.0000000000000002 ***
income -0.075144 0.001035 -72.58 <0.0000000000000002 ***
temperature 1.005855 0.030953 32.50 <0.0000000000000002 ***
health 0.148362 0.010668 13.91 <0.0000000000000002 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 5.244 on 1747 degrees of freedom
Multiple R-squared: 0.8852, Adjusted R-squared: 0.8849
F-statistic: 3366 on 4 and 1747 DF, p-value: < 0.00000000000000022
Según estos resultados, el uso de un mosquitero causa una disminución
de 10.44 puntos en el riesgo de paludismo, en promedio. Nótese que
debido a que hemos ajustado los confusores, ahora podemos usar
justificadamente el lenguaje causal en lugar de hablar simplemente de
asociaciones.
Podemos hablar de causalidad después de una regresión lineal cuando
se han ajustado los confusores porque estos se incorporan explícitamente
como variables adicionales en el modelo. Al incluirlos, se controla
estadísticamente su influencia tanto sobre la variable independiente
como sobre la dependiente, lo que permite “bloquear” los caminos
espurios que podrían generar asociaciones engañosas. Este ajuste se
realiza al estimar el efecto de la variable de interés mientras se
mantienen constantes los confusores, lo que simula un escenario de
intervención. Así, el coeficiente estimado refleja el efecto directo de
la variable explicativa sobre la respuesta, justificando el uso de
lenguaje causal.
Emparejamiento
(Matching)
El principal problema de utilizar datos de observación en lugar de
datos experimentales es que las personas que utilizaron un mosquitero lo
hicieron sin ser asignadas a un grupo de tratamiento. Las
características individuales llevaron a las personas a
auto-seleccionarse para el tratamiento, lo que hace que las personas que
usaron mosquiteros sean esencialmente diferentes de las que no lo
hicieron. Gracias al DAG, conocemos muchos de los factores que causaron
que la gente eligiera usar los mosquiteros: ingresos, salud y
temperatura.
El objetivo del código es emparejar las observaciones del dataset
mosquito_nets entre los grupos tratados (personas que recibieron una red
antimosquitos, representado por net) y no tratados, basándose en las
covariables income, temperature, y health. Este emparejamiento busca
reducir el sesgo al balancear las covariables entre los dos grupos para
estimar correctamente el efecto del tratamiento (net).
Nota: recordemos que una covariable es una
característica o variable que puede influir tanto en el tratamiento como
en el resultado que se quiere estudiar.
El emparejamiento en el código que emplea MatchIt se basa en la idea
de crear grupos tratados y no tratados que sean comparables entre sí.
Esto se logra al seleccionar observaciones de ambos grupos que sean
similares en términos de las covariables que podrían confundir la
relación entre el tratamiento (net) y el resultado. Este proceso emula
un experimento controlado aleatorizado en un estudio observacional
Ejemplo de MatchIt: un ejemplo sencillo para
ilustrar el emparejamiento con covariables:
Imagina que estás evaluando si entregar una red antimosquitos
(tratamiento) mejora la salud de las personas (resultado). Sin embargo,
las personas con mayor ingreso (income) o que viven en zonas con más
mosquitos (temperature) podrían tener más probabilidades de recibir la
red y también de tener mejor o peor salud, independientemente del
tratamiento. Entonces, si comparas directamente a tratados y no
tratados, podrías confundir el efecto real de la red con el efecto de
esas otras variables.
Para evitar eso, el emparejamiento busca, por ejemplo, una persona
tratada con ingreso de $500, temperatura de 28°C y salud regular, y la
empareja con una persona no tratada con características muy similares.
Así, al comparar sus resultados de salud, puedes atribuir con mayor
confianza cualquier diferencia al efecto de la red, porque las otras
condiciones están equilibradas. Esto es lo que se busca al emular un
experimento aleatorizado en un estudio observacional.
Continuado con el analisis
La similitud se mide utilizando la distancia de Mahalanobis, que
calcula la distancia multidimensional entre observaciones considerando
las correlaciones y escalas de las covariables. Observaciones más
cercanas en esta métrica son consideradas más similares.
# Indicamos semilla de reproducibilidad
set.seed(123)
# Indicamos modelo de emparejamiento
matched <- matchit(net ~ income + temperature + health, # Definir covariables
data = mosquito_nets, # Definir conjunto de datos
method = "nearest", # Emperejamiento por vecinos mas cercanos
distance = "mahalanobis", # Tipo de distancia multidimensional
replace = TRUE # permitir reutilizacion para emparejamiento
)
summary(matched)
Call:
matchit(formula = net ~ income + temperature + health, data = mosquito_nets,
method = "nearest", distance = "mahalanobis", replace = TRUE)
Summary of Balance for All Data:
Summary of Balance for Matched Data:
Sample Sizes:
Este código realiza un emparejamiento entre personas que recibieron
una red antimosquitos y las que no, utilizando el paquete MatchIt en R.
Lo hace comparando las covariables income, temperature y health, que
podrían influir tanto en la asignación del tratamiento como en el
resultado. Usa el método de vecinos más cercanos (method = “nearest”)
con la distancia de Mahalanobis, que considera la correlación entre
variables, para encontrar pares lo más similares posible. Además,
permite que una misma persona no tratada sea reutilizada en varios
emparejamientos (replace = TRUE), lo que mejora la calidad del
emparejamiento cuando hay pocos controles disponibles. El objetivo es
crear grupos comparables y reducir el sesgo para estimar el efecto
causal del tratamiento.
En el resultado de Sample Sizes
tenemos que:
Había 1071 controles y 681 tratados antes del
emparejamiento.
Se utilizaron 436 observaciones del grupo control para emparejar
a las 681 del grupo tratado.
Dado que replace = TRUE, algunas observaciones del grupo control
se reutilizaron para emparejar a varios tratados.
635 observaciones del grupo control no pudieron ser emparejadas.
Discarded:
No se descartaron observaciones en este análisis.
El valor Matched (ESS), o Tamaño Efectivo de la Muestra (por sus
siglas en ingles), representa una estimación ajustada del número de
unidades de control emparejadas, teniendo en cuenta que algunas de ellas
pueden haber sido reutilizadas varias veces durante el emparejamiento
(porque se usó replace = TRUE). Aunque el número real de controles
emparejados fue 436, el ESS fue de 320.94, lo que indica que la
reutilización de controles reduce la diversidad efectiva del grupo de
comparación, y por tanto, el poder estadístico del análisis. Es una
forma más precisa de reflejar cuánta “información independiente” se
tiene realmente en el grupo de control emparejado
El emparejamiento fue exitoso, logrando balancear las covariables
entre los dos grupos, lo que permitirá estimar el efecto del tratamiento
(net) con menor sesgo.
Ahora,podemos crear una nueva base de datos basada en este matching
con la función match.data()
. Observa cómo ahora hay sólo
1117 filas en lugar de 1752, ya que hemos descartado las observaciones
no emparejadas. También ahora hay una nueva columna llamada weights. La
función matchit()
asigna a los pares de observaciones
emparejadas diferentes pesos dependiendo de lo cerca o lejos que estén
los emparejamientos en un intento de controlar la variación de la
distancia. Podemos utilizar estos pesos en nuestro modelo de regresión
para mejorar nuestra estimación del efecto causal
mosquito_nets_matched <- match.data(matched)
glimpse(mosquito_nets_matched)
Rows: 1,117
Columns: 11
$ id <dbl> 1, 3, 4, 5, 6, 7, 8, 11, 12, 13, 14, 16, 17, 20, 22, 23, 24, 25, 27, 28, 31, 33, 35, 36, 40, 43, 44, 4…
$ net <lgl> TRUE, FALSE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, TRUE, FALSE, TRUE, TRUE, TRUE, FALSE, FALSE…
$ net_num <dbl> 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, …
$ malaria_risk <dbl> 33, 80, 34, 44, 25, 19, 35, 30, 14, 31, 34, 54, 23, 65, 59, 41, 21, 25, 23, 26, 50, 29, 66, 27, 21, 21…
$ income <dbl> 781, 502, 671, 728, 1050, 1146, 1093, 889, 1133, 807, 950, 594, 936, 431, 655, 896, 1033, 1115, 967, 8…
$ health <dbl> 56, 15, 20, 17, 48, 65, 75, 75, 62, 42, 53, 29, 47, 6, 30, 45, 64, 62, 71, 37, 37, 53, 22, 67, 40, 53,…
$ household <dbl> 2, 3, 5, 5, 1, 3, 5, 6, 3, 4, 3, 5, 2, 6, 4, 3, 7, 3, 3, 3, 6, 4, 1, 3, 2, 3, 2, 2, 2, 2, 5, 1, 1, 2, …
$ eligible <lgl> FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, TRUE, FALSE, …
$ temperature <dbl> 21.1, 25.6, 21.3, 19.2, 25.3, 27.4, 29.8, 17.4, 18.9, 27.6, 24.0, 25.5, 26.1, 26.8, 23.6, 25.9, 23.4, …
$ resistance <dbl> 59, 65, 46, 54, 34, 45, 65, 33, 39, 37, 53, 55, 32, 38, 43, 53, 51, 33, 52, 71, 59, 52, 84, 31, 63, 53…
$ weights <dbl> 1.0000000, 0.6402349, 1.0000000, 0.6402349, 1.2804699, 1.0000000, 1.9207048, 0.6402349, 1.0000000, 1.0…
Por ultimo,podemos hacer una regresión usando los
datos emparejados:
# Indicamos modelo lineal
model_matched <- lm(malaria_risk ~ net, data = mosquito_nets_matched,
weights = weights)
# Vemos resumen estadistico
summary(model_matched)
Call:
lm(formula = malaria_risk ~ net, data = mosquito_nets_matched,
weights = weights)
Weighted Residuals:
Min 1Q Median 3Q Max
-38.295 -7.605 -2.365 5.395 55.395
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 36.0896 0.5971 60.45 <0.0000000000000002 ***
netTRUE -10.4846 0.7647 -13.71 <0.0000000000000002 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 12.47 on 1115 degrees of freedom
Multiple R-squared: 0.1443, Adjusted R-squared: 0.1435
F-statistic: 188 on 1 and 1115 DF, p-value: < 0.00000000000000022
Según estos resultados, el uso de un mosquitero causa una disminución
de 10.48 puntos en el riesgo de malaria, en promedio. Una vez más,
podemos utilizar el lenguaje causal ahora porque hemos ajustado los
confusores al hacer la comparación, identificando así el camino causal
entre el uso de mosquiteros y el riesgo de malaria.
Ponderación de
probabilidad inversa
Una desventaja del uso de matching es que desechamos mucha
información: las observaciones no coincidentes se descartan, y el tamaño
de nuestra muestra puede reducirse significativamente. El emparejamiento
también tiende a ser muy riguroso, ya que cada observación tratada debe
ser emparejada con una (o más) observaciones no tratadas. Aunque que
algunas observaciones no tratadas están en realidad muy cerca de las
observaciones tratadas, aún así se descartan porque fueron superadas por
observaciones que tienen una distancia ligeramente menor.
En lugar de desechar datos potencialmente útiles, podemos utilizar
otros métodos para crear coincidencias que sean menos rigurosas pero más
informativas. Un método común en epidemiología y bioestadística es la
ponderación de probabilidad inversa (IPW, en inglés). En la IPW, a cada
observación se le asigna un peso basado en lo bien que su asignación
real al tratamiento coincide con la probabilidad prevista del
tratamiento, y esos pesos se utilizan luego en un modelo de regresión
para estimar el efecto causal del tratamiento en el resultado.
Primero, podemos ajustar los factores de confusión
de nuestro mosquitero y el ejemplo de riesgo de malaria usando una
ponderación de probabilidad inversa.
Para ello, usamos una regresión logística para predecir la propensión
a usar un mosquitero usando los ingresos, la temperatura y la salud.
Luego usamos los puntajes de propensión para calcular los pesos de
probabilidad inversa usando esta fórmula:
\[
\frac{\text{Mosquitero}}{\text{Propensión}} + \frac{1 -
\text{Mosquitero}}{1 - \text{Propensión}}
\]
# Creamos regresion logistica
model_mosquito_net <- glm(net ~ income + temperature + health,
data = mosquito_nets, family = binomial(link = "logit"))
# Agregamos ipw
mosquito_nets_ipw <- mosquito_nets %>% # Indicamos el df
mutate(propensity = predict(model_mosquito_net, type = "response"), #Aplicamos el modelo logistico, con las probabilidades
ipw = (net_num / propensity) + (1 - net_num) / (1 - propensity)) # creamos columna ipw
# Vemos resultados
print(mosquito_nets_ipw)
Este código ajusta un modelo de regresión logística para estimar la
probabilidad de que una persona reciba un mosquitero (net
)
en función de sus ingresos, temperatura y salud, y luego utiliza esas
probabilidades (propensiones) para calcular pesos de ponderación por la
inversa de la probabilidad de tratamiento (IPW). Estos pesos permiten
corregir posibles sesgos de selección al comparar grupos tratados y no
tratados, asignando mayor peso a las observaciones que recibieron un
tratamiento inesperado según sus características. Así, se busca simular
un escenario más parecido a un experimento aleatorio y estimar de forma
más justa el efecto del tratamiento.
Segundo, ahora que tenemos pesos de probabilidad
inversa, podemos usarlos en una regresión:
# Indicamos modelo lineal
model_ipw <- lm(malaria_risk ~ net, data = mosquito_nets_ipw,
weights = ipw)
# Vemos resultados
summary(model_ipw)
Call:
lm(formula = malaria_risk ~ net, data = mosquito_nets_ipw, weights = ipw)
Weighted Residuals:
Min 1Q Median 3Q Max
-45.705 -14.622 -4.924 10.791 162.642
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 39.6788 0.4684 84.71 <0.0000000000000002 ***
netTRUE -10.1312 0.6583 -15.39 <0.0000000000000002 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 19.54 on 1750 degrees of freedom
Multiple R-squared: 0.1192, Adjusted R-squared: 0.1187
F-statistic: 236.8 on 1 and 1750 DF, p-value: < 0.00000000000000022
Según este modelo de IPW, el uso de un mosquitero causa una
disminución de 10.13 puntos en el riesgo de malaria, en promedio. Una
vez más, podemos utilizar con seguridad el lenguaje causal porque hemos
identificado el camino causal entre el uso de mosquiteros y el riesgo de
malaria teniendo en cuenta los factores de confusión en los pesos de
probabilidad inversos.
Comparando todos los
métodos
Ahora que hemos ejecutado varios modelos de regresión que se ajustan
a los factores de confusión de diferentes maneras, podemos comparar los
resultados todos juntos. La ingenua estimación de -16 parece ser
definitivamente una sobreestimación: después de ajustar con la
regresión, el matching y la ponderación de probabilidad inversa, el
efecto causal del uso de un mosquitero en el riesgo de malaria es
consistentemente de alrededor de -10. Asumiendo que nuestro DAG está
correcto, encontramos con éxito un efecto causal a partir de datos no
experimentales y observacionales.
texreg::screenreg(list(model_naive, model_regression,
model_matched, model_ipw),
custom.model.names = c("Naive", "Regression",
"Matching", "IPW"))
===============================================================
Naive Regression Matching IPW
---------------------------------------------------------------
(Intercept) 41.94 *** 76.16 *** 36.09 *** 39.68 ***
(0.40) (0.93) (0.60) (0.47)
netTRUE -16.33 *** -10.44 *** -10.48 *** -10.13 ***
(0.65) (0.26) (0.76) (0.66)
income -0.08 ***
(0.00)
temperature 1.01 ***
(0.03)
health 0.15 ***
(0.01)
---------------------------------------------------------------
R^2 0.27 0.89 0.14 0.12
Adj. R^2 0.27 0.88 0.14 0.12
Num. obs. 1752 1752 1117 1752
===============================================================
*** p < 0.001; ** p < 0.01; * p < 0.05
LS0tCnRpdGxlOiAiSW5mZXJlbmNpYSBjYXVzYWwgYXBsaWNhZGEiCmF1dGhvcjogIkp1YW4gSm9zw6kgRWNoZXZlcnJ5IgpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHRydWUgICAgICAgICAgIyBBY3RpdmEgdGFibGEgZGUgY29udGVuaWRvCiAgICB0b2NfZGVwdGg6IDMgICAgICAgIyBQcm9mdW5kaWRhZCBkZSBsYSBUT0MgKHBvciBkZWZlY3RvIGVzIDMpCiAgICB0b2NfZmxvYXQ6IHRydWUgICAgIyBUYWJsYSBkZSBjb250ZW5pZG8gZmxvdGFudGUKICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZSAgIyBOdW1lcmEgc2VjY2lvbmVzCiAgICBmaWdfd2lkdGg6IDcgICAgICAgIyBBbmNobyBwcmVkZXRlcm1pbmFkbyBkZSBmaWd1cmFzIChlbiBwdWxnYWRhcykKICAgIGZpZ19oZWlnaHQ6IDUgICAgICAjIEFsdG8gcHJlZGV0ZXJtaW5hZG8gZGUgZmlndXJhcyAoZW4gcHVsZ2FkYXMpCiAgICAjdGhlbWU6IHVuaXRlZCAgICAgICMgVGVtYSB2aXN1YWwgZGUgQm9vdHN0cmFwIChvcGNpb25hbCkKICAgIGhpZ2hsaWdodDogdGFuZ28gICAgIyBUZW1hIGRlIHJlc2FsdGFkbyBkZSBjw7NkaWdvCi0tLQoKYGBge3Igbm8gdmlzaWJsZSwgaW5jbHVkZT1GQUxTRX0Kb3B0aW9ucyhzY2lwZW4gPSA5OTkpCgpgYGAKCiMgSW50cm9kdWNjaW9uCgpMYSBpbmZlcmVuY2lhIGNhdXNhbCBlcyB1bmEgbWV0b2RvbG9nw61hIGVzdGFkw61zdGljYSB5IGFuYWzDrXRpY2EgcXVlIGJ1c2NhIGlkZW50aWZpY2FyIHJlbGFjaW9uZXMgZGUgY2F1c2EgeSBlZmVjdG8gZW50cmUgdmFyaWFibGVzLCBtw6FzIGFsbMOhIGRlIHNpbXBsZXMgY29ycmVsYWNpb25lcy4gU3Ugb2JqZXRpdm8gZXMgcmVzcG9uZGVyIHByZWd1bnRhcyBkZWwgdGlwbyDigJzCv3F1w6kgcGFzYXLDrWEgc2nigKY/4oCdLCBwZXJtaXRpZW5kbyBlc3RpbWFyIGVsIGltcGFjdG8gZGUgdW5hIGludGVydmVuY2nDs24gbyBjYW1iaW8gZW4gdW5hIHZhcmlhYmxlIHNvYnJlIG90cmEuIEVzdGEgdMOpY25pY2EgZXMgYW1wbGlhbWVudGUgdXRpbGl6YWRhIGVuIGNhbXBvcyBjb21vIGxhIGVjb25vbcOtYSwgbGEgbWVkaWNpbmEsIGxhcyBjaWVuY2lhcyBzb2NpYWxlcywgZWwgbWFya2V0aW5nIHkgZWwgYXByZW5kaXphamUgYXV0b23DoXRpY28sIGRvbmRlIGVzIGNydWNpYWwgZW50ZW5kZXIgY8OzbW8gY2llcnRhcyBhY2Npb25lcyBvIHBvbMOtdGljYXMgaW5mbHV5ZW4gZW4gbG9zIHJlc3VsdGFkb3Mgb2JzZXJ2YWRvcy4gUG9yIGVqZW1wbG8sIHB1ZWRlIGVtcGxlYXJzZSBwYXJhIGV2YWx1YXIgZWwgZWZlY3RvIGRlIHVuYSBwb2zDrXRpY2EgcMO6YmxpY2EsIGVsIGltcGFjdG8gZGUgdW4gdHJhdGFtaWVudG8gbcOpZGljbyBvIGVsIHJlbmRpbWllbnRvIGRlIHVuYSBlc3RyYXRlZ2lhIGNvbWVyY2lhbC4KCkVuIGVzdGUgY2FzbywgYXN1bWlyZW1vcyBxdWUgYSB1bm9zIGludmVzdGlnYWRvcmVzIGxlcyBpbnRlcmVzYSBzYWJlciBzaSBlbCB1c28gZGUgbW9zcXVpdGVyb3MgZGlzbWludXllIGVsIHJpZXNnbyBkZSBxdWUgdW5hIHBlcnNvbmEgY29udHJhaWdhIG1hbGFyaWEuIFBhcmEgZWxsbyBoYW4gcmVjb3BpbGFkbyBkYXRvcyBkZSAxLjc1MiBob2dhcmVzIGVuIHVuIHBhw61zIHNpbiBub21icmUgeSB0aWVuZW4gdmFyaWFibGVzIHJlbGFjaW9uYWRhcyBjb24gbG9zIGZhY3RvcmVzIGFtYmllbnRhbGVzLCBzYWx1ZCBpbmRpdmlkdWFsIHkgbGFzIGNhcmFjdGVyw61zdGljYXMgZGUgbG9zIGhvZ2FyZXMuIEFkZW3DoXMsIGVzdGUgcGHDrXMgdGllbmUgdW4gcHJvZ3JhbWEgZXNwZWNpYWwgcXVlIHByb3BvcmNpb25hIG1vc3F1aXRlcm9zIGdyYXR1aXRvcyBhIGxvcyBob2dhcmVzIHF1ZSBjdW1wbGVuIGNvbiByZXF1aXNpdG9zIGVzcGVjw61maWNvczogcGFyYSBjYWxpZmljYXIgcGFyYSBlbCBwcm9ncmFtYSwgZGViZSBoYWJlciBtw6FzIGRlIDQgbWllbWJyb3MgZGVsIGhvZ2FyLCB5IGVsIGluZ3Jlc28gbWVuc3VhbCBkZWwgaG9nYXIgZGViZSBzZXIgaW5mZXJpb3IgYSA3MDAgZMOzbGFyZXMgYWwgbWVzLiBMb3MgaG9nYXJlcyBubyBzZSBpbnNjcmliZW4gYXV0b23DoXRpY2FtZW50ZSBlbiBlbCBwcm9ncmFtYSwgeSBtdWNob3Mgbm8gbG8gdXNhbi4gTG9zIGRhdG9zIG5vIHNvbiBleHBlcmltZW50YWxlcyAtIGxvcyBpbnZlc3RpZ2Fkb3JlcyBubyB0aWVuZW4gY29udHJvbCBzb2JyZSBxdWnDqW4gdXNhIGxvcyBtb3NxdWl0ZXJvcywgeSBsb3MgaG9nYXJlcyBpbmRpdmlkdWFsZXMgdG9tYW4gc3VzIHByb3BpYXMgZGVjaXNpb25lcyBzb2JyZSBzaSBzb2xpY2l0YW4gbW9zcXVpdGVyb3MgZ3JhdHVpdG9zIG8gY29tcHJhbiBzdXMgcHJvcGlvcyBtb3NxdWl0ZXJvcywgYXPDrSBjb21vIHNpIHVzYW4gbG9zIG1vc3F1aXRlcm9zIHNpIGxvcyB0aWVuZW4uCgojIEltcG9ydGFtb3MgbGlicmVyaWFzCgpgYGB7ciBMaWJyZXJpYXMgbmVjZXNhcmlhcywgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeShkYWdpdHR5KSAgICMgUGFyYSBjcmVhciwgYW5hbGl6YXIgeSBldmFsdWFyIGRpYWdyYW1hcyBjYXVzYWxlcyAoREFHcykKbGlicmFyeShnZ2RhZykgICAgICMgRXh0ZW5zacOzbiBkZSBnZ3Bsb3QyIHBhcmEgdmlzdWFsaXphciB5IHBlcnNvbmFsaXphciBEQUdzCmxpYnJhcnkodGlkeXZlcnNlKSAjIENvbmp1bnRvIGRlIGhlcnJhbWllbnRhcyBwYXJhIG1hbmlwdWxhY2nDs24sIHRyYW5zZm9ybWFjacOzbiB5IGFuw6FsaXNpcyBkZSBkYXRvcwpsaWJyYXJ5KGdncGxvdDIpICAgIyBQYXJhIGNyZWFyIGdyw6FmaWNvcyBlbGVnYW50ZXMgeSBwZXJzb25hbGl6YWJsZXMgZW4gUgpsaWJyYXJ5KE1hdGNoSXQpICAgIyBQYXJhIHJlYWxpemFyIGVtcGFyZWphbWllbnRvIGRlIG11ZXN0cmFzIChtYXRjaGluZykgZW4gYW7DoWxpc2lzIGNhdXNhbApsaWJyYXJ5KFhSZWcpICMgU2FsaWRhIGRlIG1vZGVsb3MgZGUgcmVncmVzaW9uCmxpYnJhcnkodGV4cmVnKSAjIFJlc3VtZW4geSBjb21wYXJhY2lvbmVzIGRlIG1vZGVsb3MKCgpgYGAKCiMgTW9kZWxhbW9zIERBRyAoRGlhZ3JhbSBBY2x5Y2xpYyBHcmFwaCkKCmBgYHtyIGZpZy5oZWlnaHQ9MTUsIGZpZy53aWR0aD0xNX0KbW9zcXVpdG9fZGFnIDwtIGRhZ2lmeSgKICByaWVzZ29fbWFsYXJpYSB+IHJlZCArIGluZ3Jlc28gKyBzYWx1ZCArIHRlbXBlcmF0dXJhICsgcmVzaXN0ZW5jaWEsCiAgcmVkICB+IGluZ3Jlc28gKyBzYWx1ZCArIHRlbXBlcmF0dXJhICsgZWxlZ2libGUgKyBob2dhciwKICBlbGVnaWJsZSB+IGluZ3Jlc28gKyBob2dhciwKICBzYWx1ZCB+IGluZ3Jlc28sCiAgZXhwb3N1cmUgPSAicmVkIiwKICBvdXRjb21lID0gInJpZXNnb19tYWxhcmlhIiwKICBjb29yZHMgPSBsaXN0KHggPSBjKHJpZXNnb19tYWxhcmlhID0gNywgcmVkICA9IDMsIGluZ3Jlc28gPSA0LCBzYWx1ZCA9IDUsCiAgICAgICAgICAgICAgICAgICAgICB0ZW1wZXJhdHVyYSA9IDYsIHJlc2lzdGVuY2lhID0gOC41LCBlbGVnaWJsZSA9IDIsIGhvZ2FyID0gMSksCiAgICAgICAgICAgICAgICB5ID0gYyhyaWVzZ29fbWFsYXJpYSA9IDIsIHJlZCAgPSAyLCBpbmdyZXNvID0gMywgc2FsdWQgPSAxLAogICAgICAgICAgICAgICAgICAgICAgdGVtcGVyYXR1cmEgPSAzLCByZXNpc3RlbmNpYSA9IDIsIGVsZWdpYmxlID0gMywgaG9nYXIgPSAyKSksCiAgbGFiZWxzID0gYyhyaWVzZ29fbWFsYXJpYSA9ICJSaWVzZ28gZGUgbWFsYXJpYSIsIHJlZCAgPSAiTW9zcXVpdGVybyIsIGluZ3Jlc28gPSAiSW5ncmVzbyIsCiAgICAgICAgICAgICBzYWx1ZCA9ICJTYWx1ZCIsIHRlbXBlcmF0dXJhID0gInRlbXBlcmF0dXJhcyBub2N0dXJuYXMiLCAKICAgICAgICAgICAgIHJlc2lzdGVuY2lhID0gIlJlc2lzdGVuY2lhIGEgaW5zZWN0aWNpZGFzIiwKICAgICAgICAgICAgIGVsZWdpYmxlID0gIkVsaWdpYmxlIHBhcmEgZWwgcHJvZ3JhbWEiLCBob2dhciA9ICJNaWVtYnJvcyBlbiBlbCBob2dhciIpCikKCmdnZGFnX3N0YXR1cyhtb3NxdWl0b19kYWcsIHVzZV9sYWJlbHMgPSAibGFiZWwiLCB0ZXh0ID0gRkFMU0UpICsgCiAgdGhlbWVfZGFnKCkKYGBgCgpFc3RlIGPDs2RpZ28gZW4gUiBjb25zdHJ1eWUgeSB2aXN1YWxpemEgdW4gZ3JhZm8gYWPDrWNsaWNvIGRpcmlnaWRvIChEQUcsIHBvciBzdXMgc2lnbGFzIGVuIGluZ2zDqXMpIHF1ZSByZXByZXNlbnRhIHJlbGFjaW9uZXMgY2F1c2FsZXMgaGlwb3TDqXRpY2FzIGVudHJlIHZhcmlhYmxlcyByZWxhY2lvbmFkYXMgY29uIGVsIHJpZXNnbyBkZSBtYWxhcmlhLiBVdGlsaXphIGxhIGZ1bmNpw7NuIGRhZ2lmeSgpIHBhcmEgZGVmaW5pciBjw7NtbyBzZSBjcmVlIHF1ZSBsYXMgdmFyaWFibGVzIGVzdMOhbiBjYXVzYWxtZW50ZSBjb25lY3RhZGFzOiBwb3IgZWplbXBsbywgZWwgdXNvIGRlIG1vc3F1aXRlcm9zIChyZWQpIGRlcGVuZGUgZGUgZmFjdG9yZXMgY29tbyBpbmdyZXNvLCBzYWx1ZCwgdGVtcGVyYXR1cmEsIGVsZWdpYmlsaWRhZCB5IHRhbWHDsW8gZGVsIGhvZ2FyLCB5IGEgc3UgdmV6IGluZmx1eWUgZW4gZWwgcmllc2dvIGRlIG1hbGFyaWEgKHJpZXNnb19tYWxhcmlhKS4gU2UgZXNwZWNpZmljYW4gbGFzIHZhcmlhYmxlcyBkZSBleHBvc2ljacOzbiAocmVkKSB5IHJlc3VsdGFkbyAocmllc2dvX21hbGFyaWEpLCBhc8OtIGNvbW8gbGFzIGNvb3JkZW5hZGFzIHBhcmEgc3UgdmlzdWFsaXphY2nDs24geSBldGlxdWV0YXMgZGVzY3JpcHRpdmFzLiBGaW5hbG1lbnRlLCBnZ2RhZ19zdGF0dXMoKSBnZW5lcmEgdW4gZ3LDoWZpY28gcXVlIG11ZXN0cmEgZWwgZXN0YWRvIGRlIGxhcyB2YXJpYWJsZXMgZW4gZWwgREFHLCDDunRpbCBwYXJhIGFuYWxpemFyIHN1cHVlc3RvcyBkZSBpZGVudGlmaWNhY2nDs24gY2F1c2FsIHkgcGxhbmVhciBlc3RyYXRlZ2lhcyBkZSBlc3RpbWFjacOzbi4KCkNhZGEgbm9kbyBlbiBlbCBEQUcgZXMgdW5hIGNvbHVtbmEgZW4gbGEgYmFzZSBkZSBkYXRvcyByZWNvZ2lkYSBwb3IgbG9zIGludmVzdGlnYWRvcmVzLCBlIGluY2x1eWUgbG8gc2lndWllbnRlOgoKKipSaWVzZ28gZGUgbWFsYXJpYSAobWFsYXJpYV9yaXNrKSoqOiBMYSBwcm9iYWJpbGlkYWQgZGUgcXVlIGFsZ3VpZW4gZW4gZWwgaG9nYXIgc2UgaW5mZWN0ZSBjb24gbWFsYXJpYS4gU2UgbWlkZSBlbiB1bmEgZXNjYWxhIGRlIDAtMTAwLCBjb24gdmFsb3JlcyBtw6FzIGFsdG9zIHF1ZSBpbmRpY2FuIHVuIG1heW9yIHJpZXNnby4KCioqTW9zcXVpdGVybyAoKG5ldCBhbmQgbmV0X251bSkqKjogVW5hIHZhcmlhYmxlIGJpbmFyaWEgcXVlIGluZGljYSBzaSBlbCBob2dhciB1dGlsaXphIG1vc3F1aXRlcm9zLgoKKipFbGVnaWJsZSBwYXJhIGVsIHByb2dyYW1hIChlbGlnaWJsZSkqKjogVW5hIHZhcmlhYmxlIGJpbmFyaWEgcXVlIGluZGljYSBzaSBlbCBob2dhciBlcyBlbGVnaWJsZSBwYXJhIGVsIHByb2dyYW1hIGRlIG1vc3F1aXRlcm9zIGdyYXR1aXRvcy4KCioqSW5ncmVzb3MgKGluY29tZSkqKjogTG9zIGluZ3Jlc29zIG1lbnN1YWxlcyBkZWwgaG9nYXIsIGVuIGTDs2xhcmVzIGFtZXJpY2Fub3MuCgoqKlRlbXBlcmF0dXJhcyBub2N0dXJuYXMgKHRlbXBlcmF0dXJlKSoqOiBMYSB0ZW1wZXJhdHVyYSBtZWRpYSBub2N0dXJuYSwgZW4gZ3JhZG9zIGNlbnTDrWdyYWRvcy4KCioqU2FsdWQgKGhlYWx0aCkqKjogTGEgc2FsdWQgZGUgbGEgZmFtaWxpYSBzZSBkZWNsYXJhZGEgcG9yIHN1cyBwcm9waW9zIG1pZW1icm9zLiBNZWRpZG8gZW4gdW5hIGVzY2FsYSBkZSAwLTEwMCwgY29uIHZhbG9yZXMgbcOhcyBhbHRvcyBxdWUgaW5kaWNhbiBtZWpvciBzYWx1ZC4KCioqTsO6bWVybyBlbiBlbCBob2dhciAoaG91c2Vob2xkKSoqOiBOw7ptZXJvIGRlIHBlcnNvbmFzIHF1ZSB2aXZlbiBlbiBlbCBob2dhci4KCioqUmVzaXN0ZW5jaWEgYSBsb3MgaW5zZWN0aWNpZGFzIChyZXNpc3RhbmNlKSoqOiBBbGd1bmFzIGNlcGFzIGRlIG1vc3F1aXRvcyBzb24gbcOhcyByZXNpc3RlbnRlcyBhIGxvcyBpbnNlY3RpY2lkYXMgeSwgcG9yIGxvIHRhbnRvLCBzdXBvbmVuIHVuIG1heW9yIHJpZXNnbyBkZSBpbmZlY3RhciBhIGxhcyBwZXJzb25hcyBjb24gbWFsYXJpYS4gRXN0byBzZSBtaWRlIGVuIHVuYSBlc2NhbGEgZGUgMC0xMDAsIGNvbiB2YWxvcmVzIG3DoXMgYWx0b3MgcXVlIGluZGljYW4gdW5hIG1heW9yIHJlc2lzdGVuY2lhLgoKU2Vnw7puIGVsIERBRywgZWwgcmllc2dvIGRlIGNvbnRyYWVyIG1hbGFyaWEgZXMgY2F1c2FkbyBwb3IgbG9zIGluZ3Jlc29zLCBsYXMgdGVtcGVyYXR1cmFzLCBsYSBzYWx1ZCwgbGEgcmVzaXN0ZW5jaWEgYSBsb3MgaW5zZWN0aWNpZGFzIHkgZWwgdXNvIGRlIG1vc3F1aXRlcm9zLiBMYXMgcGVyc29uYXMgcXVlIHZpdmVuIGVuIHJlZ2lvbmVzIG3DoXMgY8OhbGlkYXMsIHRpZW5lbiBpbmdyZXNvcyBtw6FzIGJham9zLCB0aWVuZW4gcGVvciBzYWx1ZCwgZXN0w6FuIHJvZGVhZGFzIGRlIG1vc3F1aXRvcyBjb24gYWx0YSByZXNpc3RlbmNpYSBhIGxvcyBpbnNlY3RpY2lkYXMgeSBsb3MgcXVlIG5vIHV0aWxpemFuIG1vc3F1aXRlcm9zIGNvcnJlbiB1biByaWVzZ28gbWF5b3IgZGUgY29udHJhZXIgbWFsYXJpYSBxdWUgbG9zIHF1ZSBsb3MgdXRpbGl6YW4uIEVsIHVzbyBkZSBsb3MgbW9zcXVpdGVyb3Mgc2UgZXhwbGljYSBwb3IgbG9zIGluZ3Jlc29zLCBsYXMgdGVtcGVyYXR1cmFzIG5vY3R1cm5hcywgbGEgc2FsdWQsIGVsIG7Dum1lcm8gZGUgcGVyc29uYXMgcXVlIHZpdmVuIGVuIGxhIHZpdmllbmRhIHkgbGEgZWxlZ2liaWxpZGFkIHBhcmEgZWwgcHJvZ3JhbWEgZGUgbW9zcXVpdGVyb3MgZ3JhdHVpdG9zLiBMYXMgcGVyc29uYXMgcXVlIHZpdmVuIGVuIHpvbmFzIG3DoXMgZnJlc2NhcyBwb3IgbGEgbm9jaGUsIHRpZW5lbiBpbmdyZXNvcyBtw6FzIGFsdG9zLCBtZWpvciBzYWx1ZCwgdGllbmVuIG3DoXMgcGVyc29uYXMgZW4gZWwgaG9nYXIsIHkgc29uIGVsZWdpYmxlcyBwYXJhIGxvcyBtb3NxdWl0ZXJvcyBncmF0dWl0b3MgZGVsIGdvYmllcm5vLCB0aWVuZW4gbcOhcyBwcm9iYWJpbGlkYWRlcyBkZSB1c2FyIHJlZ3VsYXJtZW50ZSBsb3MgbW9zcXVpdGVyb3MuIEVsIERBRyB0YW1iacOpbiBtdWVzdHJhIHF1ZSBsYSBlbGVnaWJpbGlkYWQgcGFyYSBlbCBwcm9ncmFtYSBkZSBtb3NxdWl0ZXJvcyBncmF0dWl0b3Mgc2UgZXhwbGljYSBwb3IgbG9zIGluZ3Jlc29zIHkgZWwgdGFtYcOxbyBkZSBsb3MgbsO6Y2xlb3MgZmFtaWxpYXJlcywgeWEgcXVlIGxvcyBob2dhcmVzIGRlYmVuIGN1bXBsaXIgY29uIHVtYnJhbGVzIGVzcGVjw61maWNvcyBwYXJhIGNhbGlmaWNhci4KCiMgQ2FyZ2Ftb3MgbGEgYmFzZSBkZSBkYXRvcwoKYGBge3J9CmxvYWQoIn4vTGlicmFyeS9Nb2JpbGUgRG9jdW1lbnRzL2NvbX5hcHBsZX5DbG91ZERvY3MvQ3Vyc29zL0NhdXNhbCBJbmZlcmVuY2UvQ2F1c2FsIEluZmVyZW5jZSBpbiBSL1IgZm9yIHBvbGl0aWNhbCBkYXRhIHNjaWVuY2UvZGF0YS9tb3NxdWl0b19uZXRzLnJkYSIpCgpnbGltcHNlKG1vc3F1aXRvX25ldHMpCmBgYAoKIyBBbmFsaXNhbW9zIGxhcyBpbmRlcGVuZGVuY2lhcyBjb25kaWNpb25hbGVzCgpFbCBjb21hbmRvIGBpbXBsaWVkQ29uZGl0aW9uYWxJbmRlcGVuZGVuY2llcygpYCBzZSB1dGlsaXphIGVuIGVsIHBhcXVldGUgZGFnaXR0eSBlbiBSIHBhcmEgYW5hbGl6YXIgdW4gREFHIChHcsOhZmljbyBBY8OtY2xpY28gRGlyaWdpZG8pLkVzdGUgY29tYW5kbyBpZGVudGlmaWNhIGxhcyBpbmRlcGVuZGVuY2lhcyBjb25kaWNpb25hbGVzIHF1ZSBkZWJlcsOtYW4gZXhpc3RpciBlbiBlbCBtb2RlbG8sIGJhc8OhbmRvc2UgZW4gc3UgZXN0cnVjdHVyYS4gVGVuaWVuZG8gZW4gY3VlbnRhIHF1ZSBlbCBzaWdubyBzZSBpbnRlcnByZXRhIGNvbW8gaW5kZXBlbmRlbmNpYSBg4oqlYC4KCkN1YW5kbyBkb3MgdmFyaWFibGVzIHNvbiBpbmRlcGVuZGllbnRlcyBpbmNvbmRpY2lvbmFsbWVudGUsIHNpZ25pZmljYSBxdWUgbm8gdGllbmVuIHJlbGFjacOzbiBkaXJlY3RhIG8gaW5kaXJlY3RhLCB5IGNvbm9jZXIgZWwgdmFsb3IgZGUgdW5hIG5vIHByb3BvcmNpb25hIGluZm9ybWFjacOzbiBzb2JyZSBsYSBvdHJhLiBQb3IgZWplbXBsbywgc2kgQSB5IEIgc29uIGluY29uZGljaW9uYWxtZW50ZSBpbmRlcGVuZGllbnRlcywgZWwgY29ub2NpbWllbnRvIGRlIEEgbm8gYWZlY3RhIG51ZXN0cmEgcHJlZGljY2nDs24gc29icmUgQi4gUG9yIG90cm8gbGFkbywgZG9zIHZhcmlhYmxlcyBzb24gaW5kZXBlbmRpZW50ZXMgY29uZGljaW9uYWxtZW50ZSBzaSBkZWphbiBkZSBlc3RhciByZWxhY2lvbmFkYXMgYWwgY29ub2NlciBlbCB2YWxvciBkZSB1bmEgdGVyY2VyYSB2YXJpYWJsZSBDLiBFbiBlc3RlIGNhc28sIEEgeSBCIHB1ZWRlbiBlc3RhciBjb3JyZWxhY2lvbmFkYXMgaW5pY2lhbG1lbnRlLCBwZXJvIGVzYSBjb3JyZWxhY2nDs24gZGVzYXBhcmVjZSBhbCBjb250cm9sYXIgcG9yIEMuCgoqKlByaW1lcm8qKiwgdmVyaWZpY2FyIGluZGVwZW5kZWNpYXMgY29uZGljaW9uYWxlcwoKYGBge3J9CmltcGxpZWRDb25kaXRpb25hbEluZGVwZW5kZW5jaWVzKG1vc3F1aXRvX2RhZykKYGBgCgpsYXMgaW5kZXBlbmRlbmNpYXMgaW5jb25kaWNpb25hbGVzIChjb21vIGVsZ2IgKlx8XHwqIHJzc3QpIHNpZ25pZmljYW4gcXVlLCBiYXPDoW5kb25vcyBlbiBsYSBlc3RydWN0dXJhIGRlbCBEQUcsIGxhcyB2YXJpYWJsZXMgIkVsaWdpYmxlIHBhcmEgZWwgcHJvZ3JhbWEiIChlbGVnaWJsZSkgeSAiUmVzaXN0ZW5jaWEgYSBpbnNlY3RpY2lkYXMiIChyZXNpc3RlbmNpYSkgbm8gdGllbmVuIHVuYSByZWxhY2nDs24gZGlyZWN0YSBvIGluZGlyZWN0YTsgY29ub2NlciBlbCBlc3RhZG8gZGUgdW5hIG5vIGNhbWJpYSBuYWRhIHNvYnJlIGxhIG90cmEuIFBvciBvdHJvIGxhZG8sIGxhcyBpbmRlcGVuZGVuY2lhcyBjb25kaWNpb25hbGVzIChjb21vIGVsZ2IgKlx8XHwqIHNhbGQgXHwgaW5ncikgaW5kaWNhbiBxdWUsIGF1bnF1ZSBwb2Ryw61hIGhhYmVyIHVuYSByZWxhY2nDs24gZW50cmUgIkVsZWdpYmxlIiB5ICJTYWx1ZCIsIGVzdGEgcmVsYWNpw7NuIGRlc2FwYXJlY2Ugc2kgY29ub2NlbW9zICJJbmdyZXNvIiwgcXVlIGFjdMO6YSBjb21vIHZhcmlhYmxlIGRlIGFqdXN0ZS4KCioqU2VndW5kbyoqLCB2YWxpZGFtb3MgY29ycmVsYWNpw7NuIGVudHJlIGxhcyB2YXJpYWJsZXMKCmBgYHtyfQoKIyBFbGVnaWJsZSDiiqUgcmVzaXN0ZW5jaWEKY29yKG1vc3F1aXRvX25ldHMkZWxpZ2libGUsbW9zcXVpdG9fbmV0cyRyZXNpc3RhbmNlKQoKIyBFbGVnaWJsZSDiiqUgdGVtcGVyYXR1cmEKY29yKG1vc3F1aXRvX25ldHMkZWxpZ2libGUsbW9zcXVpdG9fbmV0cyR0ZW1wZXJhdHVyZSkKCiMgTWllbWJyb3MgSG9nYXIg4oqlIEluZ3Jlc28KY29yKG1vc3F1aXRvX25ldHMkaG91c2Vob2xkLG1vc3F1aXRvX25ldHMkaW5jb21lKQoKIyBNaWVtYnJvcyBIb2dhciDiiqUgUmVzaXN0ZW5jaWEKY29yKG1vc3F1aXRvX25ldHMkaG91c2Vob2xkLG1vc3F1aXRvX25ldHMkcmVzaXN0YW5jZSkKCiMgTWllbWJyb3MgSG9nYXIg4oqlIFNhbHVkCmNvcihtb3NxdWl0b19uZXRzJGhvdXNlaG9sZCxtb3NxdWl0b19uZXRzJGhlYWx0aCkKCiMgTWllbWJyb3MgSG9nYXIg4oqlIHRlbXBlcmF0dXJhCmNvcihtb3NxdWl0b19uZXRzJGhvdXNlaG9sZCxtb3NxdWl0b19uZXRzJHRlbXBlcmF0dXJlKQoKIyAgSW5ncmVzbyDiiqUgcmVzaXN0ZW5jaWFzCmNvcihtb3NxdWl0b19uZXRzJGluY29tZSxtb3NxdWl0b19uZXRzJHJlc2lzdGFuY2UpCgojICBJbmdyZXNvIOKKpSB0ZW1wZXJhdHVyYQpjb3IobW9zcXVpdG9fbmV0cyRpbmNvbWUsbW9zcXVpdG9fbmV0cyR0ZW1wZXJhdHVyZSkKCiMgUmVkIOKKpSByZXNpc3RlbmNpYQpjb3IobW9zcXVpdG9fbmV0cyRuZXRfbnVtLG1vc3F1aXRvX25ldHMkcmVzaXN0YW5jZSkKCiMgcmVzaXN0ZW5jaWEg4oqlIHNhbHVkCmNvcihtb3NxdWl0b19uZXRzJHJlc2lzdGFuY2UsbW9zcXVpdG9fbmV0cyRoZWFsdGgpCgojIHJlc2lzdGVuY2lhIOKKpSB0ZW1wZXJhdHVyYQpjb3IobW9zcXVpdG9fbmV0cyRyZXNpc3RhbmNlLG1vc3F1aXRvX25ldHMkdGVtcGVyYXR1cmUpCgojIHNhbHVkIOKKpSB0ZW1wZXJhdHVyYQpjb3IobW9zcXVpdG9fbmV0cyRoZWFsdGgsbW9zcXVpdG9fbmV0cyR0ZW1wZXJhdHVyZSkKCmBgYAoKUG9kZW1vcyBxdWUgb2JzZXJ2YXIgcXVlIGxhIGNvcnJlbGFjacOzbiBlbnRyZSBsYXMgdmFyaWFibGVzIGluZGVwZW5kaWVudGVzIGluY29uZGljaW9uYWRhcyBlcyBzaWduaWZpY2F0aXZhbWVudGUgYmFqYXMuIFN1Z2lyaWVuZG8gbGEgYXVzZW5jaWEgZGUgZGVwZW5kZW5jaWFzIGVudHJlIGVsbGFzCgojIEV2YWx1YW1vcyBsYSBpbmRlcGVuZGVuY2lhIGNvbmRpY2lvbmFsCgpFbCByaWVzZ28gZGUgbWFsYXJpYSBkZWJlIHNlciBpbmRlcGVuZGllbnRlIGRlbCBuw7ptZXJvIGRlIG1pZW1icm9zIGRlIGxhIGZhbWlsaWEsIGRhZG8gcXVlIGxvcyBuaXZlbGVzIGRlIHNhbHVkLCBpbmdyZXNvcywgdXNvIGRlIG1vc3F1aXRlcm9zIHkgdGVtcGVyYXR1cmFzIG5vY3R1cm5hcyBzb24gc2ltaWxhcmVzLgoKTm8gcG9kZW1vcyB1dGlsaXphciBjb3IoKSBwYXJhIHByb2JhciBlc3RhIGltcGxpY2FjacOzbiwgeWEgcXVlIGhheSBtdWNoYXMgdmFyaWFibGVzIGVuIGp1ZWdvLCBwZXJvIHBvZGVtb3MgdXRpbGl6YXIgdW4gbW9kZWxvIGRlIHJlZ3Jlc2nDs24gcGFyYSBjb21wcm9iYXIgc2kgZWwgbsO6bWVybyBkZSBtaWVtYnJvcyBkZWwgaG9nYXIgZXN0w6Egc2lnbmlmaWNhdGl2YW1lbnRlIHJlbGFjaW9uYWRvIGNvbiBlbCByaWVzZ28gZGUgbWFsYXJpYQoKYGBge3J9CiMgUmllc2dvIGRlIG1hbGFyaWEg4oqlIE1pZW1icm9zIGVuIGVsIGhvZ2FyfCBTYWx1ZCwgSW5ncmVzbywgVXNvIGRlIGxhIHJlZCwgVGVtcGVyYXR1cmE6CmxtKG1hbGFyaWFfcmlzayB+IGhvdXNlaG9sZCArIGhlYWx0aCArIGluY29tZSArIG5ldF9udW0gICsgdGVtcGVyYXR1cmUsIAogICBkYXRhID0gbW9zcXVpdG9fbmV0cykgJT4lIAogIHN1bW1hcnkoKQpgYGAKCk5vIGVzIHNpZ25pZmljYXRpdm8gKHQ9IOKIkjAuMTcsIHAgPSAwLjg2MyksIGxvIHF1ZSBzaWduaWZpY2EgcXVlIGxvcyBkb3Mgc29uIGluZGVwZW5kaWVudGVzLCBjb21vIHNlIGVzcGVyYWJhLiBQb3IgbG8gdGFudG8sIGxhIHZhcmlhYmxlIHJpZXNnb3MgZGUgbWFsYXJpYSBlcyBpbmRlcGVuZGllbnRlIGEgbGEgdmFyaWFibGUgbWllbWJyb3MgZGVsIGhvZ2FyLgoKU2kgaGF5IGNvcnJlbGFjaW9uZXMgc3VzdGFuY2lhbGVzIHkgc2lnbmlmaWNhdGl2YXMgZW50cmUgbG9zIG5vZG9zIHF1ZSBkZWJlcsOtYW4gc2VyIGluZGVwZW5kaWVudGVzLCBlcyBwcm9iYWJsZSBxdWUgaGF5YSB1biBwcm9ibGVtYSBjb24gbGEgZXNwZWNpZmljYWNpw7NuIGRlbCBEQUcuCgpQYXJhIGVzdGUgZWplbXBsbyBlbiBjb25jcmV0byBoZW1vcyB2YWxpZGFkbyBxdWUgbm8gaGF5IGRlcGVuZGVuY2lhIHNpZ25pZmljYXRpdmFzIHkgcXVlIG51ZXN0cm8gREFHIGNhbGN1bGEgbGEgcmVhbGl6YWQgZGVsIG1vZGVsby4KCiMgRWNvbnRyYW1vcyBlbCBzZXQgZGUgYWp1c3RlCgpFeGlzdGUgdW5hIHbDrWEgZGlyZWN0YSBlbnRyZSBlbCB1c28gZGUgbW9zcXVpdGVyb3MgeSBlbCByaWVzZ28gZGUgY29udHJhZXIgbWFsYXJpYSwgcGVybyBlbCBlZmVjdG8gbm8gc2UgaWRlbnRpZmljYSBjYXVzYWxtZW50ZSBkZWJpZG8gYSBvdHJhcyB2YXJpYXMgdsOtYXMgYWJpZXJ0YXMuIFBvZGVtb3MgZGVqYXIgcXVlIFIgZW5jdWVudHJlIGxvcyBzZXRzIGRlIGFqdXN0ZSBhcHJvcGlhZG9zIGF1dG9tw6F0aWNhbWVudGU6CgpgYGB7cn0KIyBDb21hbmRvIHBhcmEgZXZ1YWx1YXIgbG9zIGNhbWlub3MgZGVsIERBRwpwYXRocyhtb3NxdWl0b19kYWcpCgpgYGAKCioqXCRwYXRocyoqCgpNdWVzdHJhIHRvZGFzIGxhcyBydXRhcyAoY2F1c2FsZXMsIGluZGlyZWN0YXMsIGVzcHVyaWFzKSBlbnRyZSB2YXJpYWJsZXMgZW4gZWwgREFHLiBFc3RvIGVzIMO6dGlsIHBhcmEgdmlzdWFsaXphciBjw7NtbyBsYXMgdmFyaWFibGVzIGVzdMOhbiBjb25lY3RhZGFzIGRpcmVjdGEgZSBpbmRpcmVjdGFtZW50ZS4KCioqXCRvcGVuKioKCkxhIGxpc3RhIFwkb3BlbiBpbmRpY2Egc2kgbGFzIHJ1dGFzIGVzdMOhbiBhYmllcnRhcyBvIGNlcnJhZGFzLiBMYXMgcnV0YXMgWzRdLCBbNV0gZXN0w6FuIGNlcnJhZGFzIChGQUxTRSksIGxvIHF1ZSBzdWdpZXJlIHF1ZSBlc3TDoW4gYmxvcXVlYWRhcyBwb3IgZWwgY29udHJvbCBkZSBjaWVydGFzIHZhcmlhYmxlcy4gUGFyYSBlbnRlbmRlciwgbGEgcnV0YSA0IGVzdMOhIGNlcnJhZGEgcG9yIGxhIHZhcmlhYmxlIGVsZWdpYmxlcywgeWEgcXVlIG5vIHBlcm1pdGUgZWwgZmx1am8gZGUgaW5ncmVzbyAodmFyaWFibGUgY29uZnVzYSkgYSBsYSB2YXJpYWJsZSB0cmF0YW1pZW50byBhIHBlc2FyIGRlIHF1ZSBzaSB0aWVuZSBpbmZsdWVuY2lhIGVuIGVsIG91dGNvbWUuCgpVbmEgcnV0YSBzZSBkZXRlcm1pbmEgYWJpZXJ0YSBvIGNlcnJhZGEgZGVwZW5kaWVuZG8gc2kgcGVybWl0ZSBlbCBmbHVqbyBjYXVzYWwuIEVzIGRlY2lyLCB1bmEgcnV0YSBlcyBhYmllcnRhIChUUlVFKSBzaSBoYXkgcmVsYWNpw7NuIGVudHJlIGxhIGV4cG9zaWNpw7NuICh0cmF0YW1pZW50bykgeSBlbCByZXN1bHRhZG8gKG91dGNvbWUpLCB5YSBzZWEgcG9yIG1lZGlvIGRlIHVuYSB2YXJpYWJsZSBjb25mdXNvciBvIGRlIGZvcm1hIGRpcmVjdGEgY29tbyBsYSBydXRhIDEuCgpFbCBzaWd1ZW50ZSBjb21hbmRvIGlkZW50aWZpY2EgbGFzIHZhcmlhYmxlcyBxdWUgZGViZW4gYWp1c3RhcnNlIHBhcmEgb2J0ZW5lciB1bmEgZXN0aW1hY2nDs24gbm8gc2VzZ2FkYSBkZWwgZWZlY3RvIGNhdXNhbC4gTGFzIHZhcmlhYmxlcyBkZSBhanVzdGUgc29uIGFxdWVsbGFzIHF1ZSAiY2llcnJhbiIgbGFzIHJ1dGFzIGRlIGNvbmZ1c2nDs24sIHBlcm1pdGllbmRvIGFuYWxpemFyIGVsIGVmZWN0byBkaXJlY3RvIGVudHJlIGxhcyB2YXJpYWJsZXMgZGUgaW50ZXLDqXMuCgpFbCBlZmVjdG8gZGUgY29uZnVzacOzbiBvY3VycmUgY3VhbmRvIGxhIHJlbGFjacOzbiBvYnNlcnZhZGEgZW50cmUgZG9zIHZhcmlhYmxlcyBlc3TDoSBpbmZsdWVuY2lhZGEgcG9yIHVuYSB0ZXJjZXJhIHZhcmlhYmxlIChsbGFtYWRhIGNvbmZ1c29yKSBxdWUgYWZlY3RhIHRhbnRvIGEgbGEgdmFyaWFibGUgaW5kZXBlbmRpZW50ZSAoZXhwb3NpY2nDs24pIGNvbW8gYSBsYSB2YXJpYWJsZSBkZXBlbmRpZW50ZSAocmVzdWx0YWRvKS4gRXN0ZSBlZmVjdG8gcHVlZGUgZGlzdG9yc2lvbmFyIG8gc2VzZ2FyIGxhIGVzdGltYWNpw7NuIGRlbCBlZmVjdG8gY2F1c2FsLCBsbGV2YW5kbyBhIGNvbmNsdXNpb25lcyBpbmNvcnJlY3Rhcy4KCmBgYHtyfQojIFZhcmlhYmxlcyBuZWNlc2FyaWFzIHBhcmEgY29udHJvbGFyIGVmZWN0byBkZSBjb25mdXNpb24KYWRqdXN0bWVudFNldHMobW9zcXVpdG9fZGFnKQpgYGAKCkJhc8OhbmRvc2UgZW4gbGFzIHJlbGFjaW9uZXMgZW50cmUgdG9kb3MgbG9zIG5vZG9zIGRlbCBEQUcsIGVsIGFqdXN0ZSBwb3Igc2FsdWQsIGluZ3Jlc29zIHkgdGVtcGVyYXR1cmEgZXMgc3VmaWNpZW50ZSBwYXJhIGNlcnJhciB0b2RhcyBsYXMgcHVlcnRhcyB0cmFzZXJhcyBlIGlkZW50aWZpY2FyIGxhIHJlbGFjacOzbiBlbnRyZSBlbCB1c28gZGUgbGEgcmVkIHkgZWwgcmllc2dvIGRlIG1hbGFyaWEuIEVzIGltcG9ydGFudGUgZGVzdGFjYXIgcXVlIG5vIHRlbmVtb3MgcXVlIHByZW9jdXBhcm5vcyBwb3IgbmluZ3VubyBkZSBsb3Mgbm9kb3MgcmVsYWNpb25hZG9zIGNvbiBlbCBwcm9ncmFtYSBndWJlcm5hbWVudGFsIGRlIHJlZGVzIGdyYXR1aXRhcywgeWEgcXVlIGVzb3Mgbm9kb3Mgbm8gZXN0w6FuIGQgY29uZWN0YWRvcyBjb24gZWwgcmllc2dvIGRlIG1hbGFyaWEuIFPDs2xvIHRlbmVtb3MgcXVlIHByZW9jdXBhcm5vcyBwb3IgbGFzIHJlbGFjaW9uZXMgZGUgY29uZnVzacOzbi4KCkVzdG8gc2lnbmlmaWNhIHF1ZSwgcGFyYSBlc3RpbWFyIGNvcnJlY3RhbWVudGUgZWwgZWZlY3RvIGNhdXNhbCBkZSBsYSBleHBvc2ljacOzbiAocG9yIGVqZW1wbG8sIHJlZCkgc29icmUgZWwgcmVzdWx0YWRvIChwb3IgZWplbXBsbywgcmllc2dvX21hbGFyaWEpLCBlcyBuZWNlc2FyaW8gY29udHJvbGFyIChvIGFqdXN0YXIpIHBvciBsYXMgdmFyaWFibGVzIGluZ3Jlc28sIHNhbHVkLCB5IHRlbXBlcmF0dXJhLCB5YSBxdWUgZXN0YXMgdmFyaWFibGVzIGluZmx1eWVuIHRhbnRvIGVuIGVsIHRyYXRhbWllbnRvIChyZWQpIGNvbW8gZW4gZWwgcmVzdWx0YWRvIChtYWxhcmlhKSwgYmFqbyBlbCB0ZXJtaW5vIGVuIGluZ2xlcyBgY29uZm91bmRlcmAuCgpQb2RlbW9zIGNvbmZpcm1hciBlc3RvIGdyw6FmaWNhbWVudGUgY29uIGdnZGFnX2FkanVzdG1lbnRfc2V0KCk6CgpgYGB7ciBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9MTB9CmdnZGFnX2FkanVzdG1lbnRfc2V0KG1vc3F1aXRvX2RhZywgc2hhZG93ID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgdXNlX2xhYmVscyA9ICJsYWJlbCIsIHRleHQgPSBGQUxTRSkKYGBgCgojIEVzdGltYWNpw7NuIGluZ2VudWEgbm8gYWp1c3RhZGEKCioqUHJpbWVybyoqLCBwb2RlbW9zIHZlciBjdcOhbCBlcyBsYSByZWxhY2nDs24gZW50cmUgZWwgdXNvIGRlIG1vc3F1aXRlcm9zIHkgZWwgcmllc2dvIGRlIG1hbGFyaWEgZW4gYXVzZW5jaWEgZGUgY3VhbHF1aWVyIGFqdXN0ZS4gU2kgY3JlYW1vcyB1biBjdWFkcm8gZGUgbGEgZGlzdHJpYnVjacOzbiBkZWwgcmllc2dvIGRlIG1hbGFyaWEgZW50cmUgbGFzIHBlcnNvbmFzIHF1ZSB1c2FuIHkgbm8gdXNhbiBtb3NxdWl0ZXJvcywgdmVtb3MgcXVlIGVsIHJpZXNnbyBtZWRpbyBlcyBzdXN0YW5jaWFsbWVudGUgbWVub3IgZW50cmUgbG9zIHF1ZSB1c2FuIG1vc3F1aXRlcm9zCgpgYGB7cn0KZ2dwbG90KG1vc3F1aXRvX25ldHMsIGFlcyh4ID0gbmV0LCB5ID0gbWFsYXJpYV9yaXNrKSkgKwogIGdlb21fYm94cGxvdCgpCgpgYGAKClBvZGVtb3MgZWplY3V0YXIgdW4gc2ltcGxlIG1vZGVsbyBkZSByZWdyZXNpw7NuIHBhcmEgbWVkaXIgbGEgZGlmZXJlbmNpYSBwcm9tZWRpbyBleGFjdGE6CgpgYGB7cn0KbW9kZWxfbmFpdmUgPC0gbG0obWFsYXJpYV9yaXNrIH4gbmV0LCBkYXRhID0gbW9zcXVpdG9fbmV0cykgCnRibF9yZWdyZXNzaW9uKG1vZGVsX25haXZlKQpzdW1tYXJ5KG1vZGVsX25haXZlKQpgYGAKClNlZ8O6biBlc3RlIG1vZGVsbywgcGFyZWNlIHF1ZSBlbCB1c28gZGUgdW4gbW9zcXVpdGVybyBlc3TDoSBhc29jaWFkbyBjb24gdW5hIGRpc21pbnVjacOzbiBkZSAxNiBwdW50b3MgZW4gZWwgcmllc2dvIGRlIG1hbGFyaWEuIFNpbiBlbWJhcmdvLCBlc3RlIG5vIGVzIGVsIGVmZWN0byBjYXVzYWwuIEVzdGUgZXMgdW4gY2FzbyBlbiBlbCBxdWUgbGEgY29ycmVsYWNpw7NuIG5vIGVzIGlndWFsIGEgbGEgY2F1c2FsaWRhZC4gT3Ryb3MgZmFjdG9yZXMgY29tbyBsb3MgaW5ncmVzb3MsIGxhIHNhbHVkIHkgbGFzIHRlbXBlcmF0dXJhcyBjb25mdW5kZW4gbGEgcmVsYWNpw7NuIGVudHJlIGVsIHVzbyBkZSBsb3MgbW9zcXVpdGVyb3MgeSBlbCByaWVzZ28KCiMgUmVncmVzaW9uZXMKClVuYSBmb3JtYSByw6FwaWRhIHkgZsOhY2lsIGRlIHRyYXRhciBkZSBhanVzdGFyIGxhcyB2YXJpYWJsZXMgZGUgY29uZnVzacOzbiBlcyBpbmNsdWlybGFzIGNvbW8gY292YXJpYWJsZXMgZW4gdW5hIHJlZ3Jlc2nDs24gbGluZWFsLiBBIHByaW1lcmEgdmlzdGEsIGVzdG8gdGllbmUgc2VudGlkbyBpbnR1aXRpdm86IGVsIG9iamV0aXZvIGRlbCBhanVzdGUgZXMgY29tcGFyYXIgbG9zIG5vZG9zIGRlIHRyYXRhbWllbnRvIHkgZGUgcmVzdWx0YWRvIGNvbiBsb3MgbWlzbW9zIHZhbG9yZXMgZGUgbG9zIGRpdmVyc29zIGNvbmZ1c29yZXMsIHkgZWwgcHJvcMOzc2l0byBkZSBsYSByZWdyZXNpw7NuIG3Dumx0aXBsZSBlcyBleHBsaWNhciBsYSB2YXJpYWNpw7NuIGRlbCByZXN1bHRhZG8gbWFudGVuaWVuZG8gY29uc3RhbnRlcyBsYXMgZGlmZXJlbnRlcyB2YXJpYWJsZXMgZXhwbGljYXRpdmFzLiBTaW4gZW1iYXJnbywgaGFjZXIgYWp1c3RlcyBkZSBjb25mdXNpw7NuIGNvbiBsYSByZWdyZXNpw7NuIGxpbmVhbCBkYXLDoSBjb21vIHJlc3VsdGFkbyByZWxhY2lvbmVzIGNhdXNhbGVzIGNvcnJlY3RhbWVudGUgaWRlbnRpZmljYWRhcyBzw7NsbyBlbiBjaXJjdW5zdGFuY2lhcyBtdXkgZXNwZWPDrWZpY2FzLiBQYXJhIHF1ZSBlbCBhanVzdGUgYmFzYWRvIGVuIGxhIHJlZ3Jlc2nDs24gZnVuY2lvbmUsIGxhcyByZWxhY2lvbmVzIGVudHJlIHRvZG9zIGxvcyBub2RvcyBkZSB0cmF0YW1pZW50bywgcmVzdWx0YWRvIHkgY29uZnVzacOzbiBkZWJlbiBzZXIgbGluZWFsZXMsIGxvIGN1YWwgZXMgZGlmw61jaWwgZGUgcHJvYmFyIHkgdmVyaWZpY2FyIGNvbiBkYXRvcyBkZSBvYnNlcnZhY2nDs24gcmVhbGVzLiBDYXNpIHNpZW1wcmUgZXMgbWVqb3IgdXRpbGl6YXIgdW5hIGRlIGxhcyBvdHJhcyB0w6ljbmljYXMgZGUgYWp1c3RlIHF1ZSBzZSBkZXNjcmliZW4gYSBjb250aW51YWNpw7NuIChtYXRjaGluZyBvIHBvbmRlcmFjacOzbiBkZSBwcm9iYWJpbGlkYWQgaW52ZXJzYSksIHlhIHF1ZSBlc29zIG3DqXRvZG9zIG5vIHNlIGJhc2FuIGVuIGVsIHN1cHVlc3RvIGRlIGxhIGxpbmVhbGlkYWQuCgpgYGB7cn0KbW9kZWxfcmVncmVzc2lvbiA8LSBsbShtYWxhcmlhX3Jpc2sgfiBuZXQgKyBpbmNvbWUgKyB0ZW1wZXJhdHVyZSArIGhlYWx0aCwKZGF0YSA9IG1vc3F1aXRvX25ldHMpCnN1bW1hcnkobW9kZWxfcmVncmVzc2lvbikKYGBgCgpTZWfDum4gZXN0b3MgcmVzdWx0YWRvcywgZWwgdXNvIGRlIHVuIG1vc3F1aXRlcm8gY2F1c2EgdW5hIGRpc21pbnVjacOzbiBkZSAxMC40NCBwdW50b3MgZW4gZWwgcmllc2dvIGRlIHBhbHVkaXNtbywgZW4gcHJvbWVkaW8uIE7Ds3Rlc2UgcXVlIGRlYmlkbyBhIHF1ZSBoZW1vcyBhanVzdGFkbyBsb3MgY29uZnVzb3JlcywgYWhvcmEgcG9kZW1vcyB1c2FyIGp1c3RpZmljYWRhbWVudGUgZWwgbGVuZ3VhamUgY2F1c2FsIGVuIGx1Z2FyIGRlIGhhYmxhciBzaW1wbGVtZW50ZSBkZSBhc29jaWFjaW9uZXMuCgpQb2RlbW9zIGhhYmxhciBkZSBjYXVzYWxpZGFkIGRlc3B1w6lzIGRlIHVuYSByZWdyZXNpw7NuIGxpbmVhbCBjdWFuZG8gc2UgaGFuIGFqdXN0YWRvIGxvcyBjb25mdXNvcmVzIHBvcnF1ZSBlc3RvcyBzZSBpbmNvcnBvcmFuIGV4cGzDrWNpdGFtZW50ZSBjb21vIHZhcmlhYmxlcyBhZGljaW9uYWxlcyBlbiBlbCBtb2RlbG8uIEFsIGluY2x1aXJsb3MsIHNlIGNvbnRyb2xhIGVzdGFkw61zdGljYW1lbnRlIHN1IGluZmx1ZW5jaWEgdGFudG8gc29icmUgbGEgdmFyaWFibGUgaW5kZXBlbmRpZW50ZSBjb21vIHNvYnJlIGxhIGRlcGVuZGllbnRlLCBsbyBxdWUgcGVybWl0ZSDigJxibG9xdWVhcuKAnSBsb3MgY2FtaW5vcyBlc3B1cmlvcyBxdWUgcG9kcsOtYW4gZ2VuZXJhciBhc29jaWFjaW9uZXMgZW5nYcOxb3Nhcy4gRXN0ZSBhanVzdGUgc2UgcmVhbGl6YSBhbCBlc3RpbWFyIGVsIGVmZWN0byBkZSBsYSB2YXJpYWJsZSBkZSBpbnRlcsOpcyBtaWVudHJhcyBzZSBtYW50aWVuZW4gY29uc3RhbnRlcyBsb3MgY29uZnVzb3JlcywgbG8gcXVlIHNpbXVsYSB1biBlc2NlbmFyaW8gZGUgaW50ZXJ2ZW5jacOzbi4gQXPDrSwgZWwgY29lZmljaWVudGUgZXN0aW1hZG8gcmVmbGVqYSBlbCBlZmVjdG8gZGlyZWN0byBkZSBsYSB2YXJpYWJsZSBleHBsaWNhdGl2YSBzb2JyZSBsYSByZXNwdWVzdGEsIGp1c3RpZmljYW5kbyBlbCB1c28gZGUgbGVuZ3VhamUgY2F1c2FsLgoKIyBFbXBhcmVqYW1pZW50byAoTWF0Y2hpbmcpCgpFbCBwcmluY2lwYWwgcHJvYmxlbWEgZGUgdXRpbGl6YXIgZGF0b3MgZGUgb2JzZXJ2YWNpw7NuIGVuIGx1Z2FyIGRlIGRhdG9zIGV4cGVyaW1lbnRhbGVzIGVzIHF1ZSBsYXMgcGVyc29uYXMgcXVlIHV0aWxpemFyb24gdW4gbW9zcXVpdGVybyBsbyBoaWNpZXJvbiBzaW4gc2VyIGFzaWduYWRhcyBhIHVuIGdydXBvIGRlIHRyYXRhbWllbnRvLiBMYXMgY2FyYWN0ZXLDrXN0aWNhcyBpbmRpdmlkdWFsZXMgbGxldmFyb24gYSBsYXMgcGVyc29uYXMgYSBhdXRvLXNlbGVjY2lvbmFyc2UgcGFyYSBlbCB0cmF0YW1pZW50bywgbG8gcXVlIGhhY2UgcXVlIGxhcyBwZXJzb25hcyBxdWUgdXNhcm9uIG1vc3F1aXRlcm9zIHNlYW4gZXNlbmNpYWxtZW50ZSBkaWZlcmVudGVzIGRlIGxhcyBxdWUgbm8gbG8gaGljaWVyb24uIEdyYWNpYXMgYWwgREFHLCBjb25vY2Vtb3MgbXVjaG9zIGRlIGxvcyBmYWN0b3JlcyBxdWUgY2F1c2Fyb24gcXVlIGxhIGdlbnRlIGVsaWdpZXJhIHVzYXIgbG9zIG1vc3F1aXRlcm9zOiBpbmdyZXNvcywgc2FsdWQgeSB0ZW1wZXJhdHVyYS4KCkVsIG9iamV0aXZvIGRlbCBjw7NkaWdvIGVzIGVtcGFyZWphciBsYXMgb2JzZXJ2YWNpb25lcyBkZWwgZGF0YXNldCBtb3NxdWl0b19uZXRzIGVudHJlIGxvcyBncnVwb3MgdHJhdGFkb3MgKHBlcnNvbmFzIHF1ZSByZWNpYmllcm9uIHVuYSByZWQgYW50aW1vc3F1aXRvcywgcmVwcmVzZW50YWRvIHBvciBuZXQpIHkgbm8gdHJhdGFkb3MsIGJhc8OhbmRvc2UgZW4gbGFzIGNvdmFyaWFibGVzIGluY29tZSwgdGVtcGVyYXR1cmUsIHkgaGVhbHRoLiBFc3RlIGVtcGFyZWphbWllbnRvIGJ1c2NhIHJlZHVjaXIgZWwgc2VzZ28gYWwgYmFsYW5jZWFyIGxhcyBjb3ZhcmlhYmxlcyBlbnRyZSBsb3MgZG9zIGdydXBvcyBwYXJhIGVzdGltYXIgY29ycmVjdGFtZW50ZSBlbCBlZmVjdG8gZGVsIHRyYXRhbWllbnRvIChuZXQpLgoKKipOb3RhKio6IHJlY29yZGVtb3MgcXVlIHVuYSBjb3ZhcmlhYmxlIGVzIHVuYSBjYXJhY3RlcsOtc3RpY2EgbyB2YXJpYWJsZSBxdWUgcHVlZGUgaW5mbHVpciB0YW50byBlbiBlbCB0cmF0YW1pZW50byBjb21vIGVuIGVsIHJlc3VsdGFkbyBxdWUgc2UgcXVpZXJlIGVzdHVkaWFyLgoKRWwgZW1wYXJlamFtaWVudG8gZW4gZWwgY8OzZGlnbyBxdWUgZW1wbGVhIE1hdGNoSXQgc2UgYmFzYSBlbiBsYSBpZGVhIGRlIGNyZWFyIGdydXBvcyB0cmF0YWRvcyB5IG5vIHRyYXRhZG9zIHF1ZSBzZWFuIGNvbXBhcmFibGVzIGVudHJlIHPDrS4gRXN0byBzZSBsb2dyYSBhbCBzZWxlY2Npb25hciBvYnNlcnZhY2lvbmVzIGRlIGFtYm9zIGdydXBvcyBxdWUgc2VhbiBzaW1pbGFyZXMgZW4gdMOpcm1pbm9zIGRlIGxhcyBjb3ZhcmlhYmxlcyBxdWUgcG9kcsOtYW4gY29uZnVuZGlyIGxhIHJlbGFjacOzbiBlbnRyZSBlbCB0cmF0YW1pZW50byAobmV0KSB5IGVsIHJlc3VsdGFkby4gRXN0ZSBwcm9jZXNvIGVtdWxhIHVuIGV4cGVyaW1lbnRvIGNvbnRyb2xhZG8gYWxlYXRvcml6YWRvIGVuIHVuIGVzdHVkaW8gb2JzZXJ2YWNpb25hbAoKKipFamVtcGxvIGRlIE1hdGNoSXQqKjogdW4gZWplbXBsbyBzZW5jaWxsbyBwYXJhIGlsdXN0cmFyIGVsIGVtcGFyZWphbWllbnRvIGNvbiBjb3ZhcmlhYmxlczoKCkltYWdpbmEgcXVlIGVzdMOhcyBldmFsdWFuZG8gc2kgZW50cmVnYXIgdW5hIHJlZCBhbnRpbW9zcXVpdG9zICh0cmF0YW1pZW50bykgbWVqb3JhIGxhIHNhbHVkIGRlIGxhcyBwZXJzb25hcyAocmVzdWx0YWRvKS4gU2luIGVtYmFyZ28sIGxhcyBwZXJzb25hcyBjb24gbWF5b3IgaW5ncmVzbyAoaW5jb21lKSBvIHF1ZSB2aXZlbiBlbiB6b25hcyBjb24gbcOhcyBtb3NxdWl0b3MgKHRlbXBlcmF0dXJlKSBwb2Ryw61hbiB0ZW5lciBtw6FzIHByb2JhYmlsaWRhZGVzIGRlIHJlY2liaXIgbGEgcmVkIHkgdGFtYmnDqW4gZGUgdGVuZXIgbWVqb3IgbyBwZW9yIHNhbHVkLCBpbmRlcGVuZGllbnRlbWVudGUgZGVsIHRyYXRhbWllbnRvLiBFbnRvbmNlcywgc2kgY29tcGFyYXMgZGlyZWN0YW1lbnRlIGEgdHJhdGFkb3MgeSBubyB0cmF0YWRvcywgcG9kcsOtYXMgY29uZnVuZGlyIGVsIGVmZWN0byByZWFsIGRlIGxhIHJlZCBjb24gZWwgZWZlY3RvIGRlIGVzYXMgb3RyYXMgdmFyaWFibGVzLgoKUGFyYSBldml0YXIgZXNvLCBlbCBlbXBhcmVqYW1pZW50byBidXNjYSwgcG9yIGVqZW1wbG8sIHVuYSBwZXJzb25hIHRyYXRhZGEgY29uIGluZ3Jlc28gZGUgXCQ1MDAsIHRlbXBlcmF0dXJhIGRlIDI4wrBDIHkgc2FsdWQgcmVndWxhciwgeSBsYSBlbXBhcmVqYSBjb24gdW5hIHBlcnNvbmEgbm8gdHJhdGFkYSBjb24gY2FyYWN0ZXLDrXN0aWNhcyBtdXkgc2ltaWxhcmVzLiBBc8OtLCBhbCBjb21wYXJhciBzdXMgcmVzdWx0YWRvcyBkZSBzYWx1ZCwgcHVlZGVzIGF0cmlidWlyIGNvbiBtYXlvciBjb25maWFuemEgY3VhbHF1aWVyIGRpZmVyZW5jaWEgYWwgZWZlY3RvIGRlIGxhIHJlZCwgcG9ycXVlIGxhcyBvdHJhcyBjb25kaWNpb25lcyBlc3TDoW4gZXF1aWxpYnJhZGFzLiBFc3RvIGVzIGxvIHF1ZSBzZSBidXNjYSBhbCBlbXVsYXIgdW4gZXhwZXJpbWVudG8gYWxlYXRvcml6YWRvIGVuIHVuIGVzdHVkaW8gb2JzZXJ2YWNpb25hbC4KCioqQ29udGludWFkbyBjb24gZWwgYW5hbGlzaXMqKgoKTGEgc2ltaWxpdHVkIHNlIG1pZGUgdXRpbGl6YW5kbyBsYSBkaXN0YW5jaWEgZGUgTWFoYWxhbm9iaXMsIHF1ZSBjYWxjdWxhIGxhIGRpc3RhbmNpYSBtdWx0aWRpbWVuc2lvbmFsIGVudHJlIG9ic2VydmFjaW9uZXMgY29uc2lkZXJhbmRvIGxhcyBjb3JyZWxhY2lvbmVzIHkgZXNjYWxhcyBkZSBsYXMgY292YXJpYWJsZXMuIE9ic2VydmFjaW9uZXMgbcOhcyBjZXJjYW5hcyBlbiBlc3RhIG3DqXRyaWNhIHNvbiBjb25zaWRlcmFkYXMgbcOhcyBzaW1pbGFyZXMuCgpgYGB7cn0KIyBJbmRpY2Ftb3Mgc2VtaWxsYSBkZSByZXByb2R1Y2liaWxpZGFkCnNldC5zZWVkKDEyMykKCiMgSW5kaWNhbW9zIG1vZGVsbyBkZSBlbXBhcmVqYW1pZW50bwptYXRjaGVkIDwtIG1hdGNoaXQobmV0IH4gaW5jb21lICsgdGVtcGVyYXR1cmUgKyBoZWFsdGgsICMgRGVmaW5pciBjb3ZhcmlhYmxlcwogICAgICAgICAgICAgICAgICAgZGF0YSA9IG1vc3F1aXRvX25ldHMsICMgRGVmaW5pciBjb25qdW50byBkZSBkYXRvcwogICAgICAgICAgICAgICAgICAgbWV0aG9kID0gIm5lYXJlc3QiLCAgIyBFbXBlcmVqYW1pZW50byBwb3IgdmVjaW5vcyBtYXMgY2VyY2Fub3MKICAgICAgICAgICAgICAgICAgIGRpc3RhbmNlID0gIm1haGFsYW5vYmlzIiwgICMgVGlwbyBkZSBkaXN0YW5jaWEgbXVsdGlkaW1lbnNpb25hbAogICAgICAgICAgICAgICAgICAgcmVwbGFjZSA9IFRSVUUgIyBwZXJtaXRpciByZXV0aWxpemFjaW9uIHBhcmEgZW1wYXJlamFtaWVudG8KICAgICAgICAgICAgICAgICAgICkKCnN1bW1hcnkobWF0Y2hlZCkgICAgICAgICAgICAgICAgICAKCmBgYAoKRXN0ZSBjw7NkaWdvIHJlYWxpemEgdW4gZW1wYXJlamFtaWVudG8gZW50cmUgcGVyc29uYXMgcXVlIHJlY2liaWVyb24gdW5hIHJlZCBhbnRpbW9zcXVpdG9zIHkgbGFzIHF1ZSBubywgdXRpbGl6YW5kbyBlbCBwYXF1ZXRlIE1hdGNoSXQgZW4gUi4gTG8gaGFjZSBjb21wYXJhbmRvIGxhcyBjb3ZhcmlhYmxlcyBpbmNvbWUsIHRlbXBlcmF0dXJlIHkgaGVhbHRoLCBxdWUgcG9kcsOtYW4gaW5mbHVpciB0YW50byBlbiBsYSBhc2lnbmFjacOzbiBkZWwgdHJhdGFtaWVudG8gY29tbyBlbiBlbCByZXN1bHRhZG8uIFVzYSBlbCBtw6l0b2RvIGRlIHZlY2lub3MgbcOhcyBjZXJjYW5vcyAobWV0aG9kID0gIm5lYXJlc3QiKSBjb24gbGEgZGlzdGFuY2lhIGRlIE1haGFsYW5vYmlzLCBxdWUgY29uc2lkZXJhIGxhIGNvcnJlbGFjacOzbiBlbnRyZSB2YXJpYWJsZXMsIHBhcmEgZW5jb250cmFyIHBhcmVzIGxvIG3DoXMgc2ltaWxhcmVzIHBvc2libGUuIEFkZW3DoXMsIHBlcm1pdGUgcXVlIHVuYSBtaXNtYSBwZXJzb25hIG5vIHRyYXRhZGEgc2VhIHJldXRpbGl6YWRhIGVuIHZhcmlvcyBlbXBhcmVqYW1pZW50b3MgKHJlcGxhY2UgPSBUUlVFKSwgbG8gcXVlIG1lam9yYSBsYSBjYWxpZGFkIGRlbCBlbXBhcmVqYW1pZW50byBjdWFuZG8gaGF5IHBvY29zIGNvbnRyb2xlcyBkaXNwb25pYmxlcy4gRWwgb2JqZXRpdm8gZXMgY3JlYXIgZ3J1cG9zIGNvbXBhcmFibGVzIHkgcmVkdWNpciBlbCBzZXNnbyBwYXJhIGVzdGltYXIgZWwgZWZlY3RvIGNhdXNhbCBkZWwgdHJhdGFtaWVudG8uCgpFbiBlbCByZXN1bHRhZG8gZGUgYFNhbXBsZSBTaXplc2B0ZW5lbW9zIHF1ZToKCi0gICBIYWLDrWEgMTA3MSBjb250cm9sZXMgeSA2ODEgdHJhdGFkb3MgYW50ZXMgZGVsIGVtcGFyZWphbWllbnRvLgoKLSAgIFNlIHV0aWxpemFyb24gNDM2IG9ic2VydmFjaW9uZXMgZGVsIGdydXBvIGNvbnRyb2wgcGFyYSBlbXBhcmVqYXIgYSBsYXMgNjgxIGRlbCBncnVwbyB0cmF0YWRvLgoKLSAgIERhZG8gcXVlIHJlcGxhY2UgPSBUUlVFLCBhbGd1bmFzIG9ic2VydmFjaW9uZXMgZGVsIGdydXBvIGNvbnRyb2wgc2UgcmV1dGlsaXphcm9uIHBhcmEgZW1wYXJlamFyIGEgdmFyaW9zIHRyYXRhZG9zLgoKLSAgIDYzNSBvYnNlcnZhY2lvbmVzIGRlbCBncnVwbyBjb250cm9sIG5vIHB1ZGllcm9uIHNlciBlbXBhcmVqYWRhcy4gRGlzY2FyZGVkOgoKLSAgIE5vIHNlIGRlc2NhcnRhcm9uIG9ic2VydmFjaW9uZXMgZW4gZXN0ZSBhbsOhbGlzaXMuCgpFbCB2YWxvciBNYXRjaGVkIChFU1MpLCBvIFRhbWHDsW8gRWZlY3Rpdm8gZGUgbGEgTXVlc3RyYSAocG9yIHN1cyBzaWdsYXMgZW4gaW5nbGVzKSwgcmVwcmVzZW50YSB1bmEgZXN0aW1hY2nDs24gYWp1c3RhZGEgZGVsIG7Dum1lcm8gZGUgdW5pZGFkZXMgZGUgY29udHJvbCBlbXBhcmVqYWRhcywgdGVuaWVuZG8gZW4gY3VlbnRhIHF1ZSBhbGd1bmFzIGRlIGVsbGFzIHB1ZWRlbiBoYWJlciBzaWRvIHJldXRpbGl6YWRhcyB2YXJpYXMgdmVjZXMgZHVyYW50ZSBlbCBlbXBhcmVqYW1pZW50byAocG9ycXVlIHNlIHVzw7MgcmVwbGFjZSA9IFRSVUUpLiBBdW5xdWUgZWwgbsO6bWVybyByZWFsIGRlIGNvbnRyb2xlcyBlbXBhcmVqYWRvcyBmdWUgNDM2LCBlbCBFU1MgZnVlIGRlIDMyMC45NCwgbG8gcXVlIGluZGljYSBxdWUgbGEgcmV1dGlsaXphY2nDs24gZGUgY29udHJvbGVzIHJlZHVjZSBsYSBkaXZlcnNpZGFkIGVmZWN0aXZhIGRlbCBncnVwbyBkZSBjb21wYXJhY2nDs24sIHkgcG9yIHRhbnRvLCBlbCBwb2RlciBlc3RhZMOtc3RpY28gZGVsIGFuw6FsaXNpcy4gRXMgdW5hIGZvcm1hIG3DoXMgcHJlY2lzYSBkZSByZWZsZWphciBjdcOhbnRhICJpbmZvcm1hY2nDs24gaW5kZXBlbmRpZW50ZSIgc2UgdGllbmUgcmVhbG1lbnRlIGVuIGVsIGdydXBvIGRlIGNvbnRyb2wgZW1wYXJlamFkbwoKRWwgZW1wYXJlamFtaWVudG8gZnVlIGV4aXRvc28sIGxvZ3JhbmRvIGJhbGFuY2VhciBsYXMgY292YXJpYWJsZXMgZW50cmUgbG9zIGRvcyBncnVwb3MsIGxvIHF1ZSBwZXJtaXRpcsOhIGVzdGltYXIgZWwgZWZlY3RvIGRlbCB0cmF0YW1pZW50byAobmV0KSBjb24gbWVub3Igc2VzZ28uCgpBaG9yYSxwb2RlbW9zIGNyZWFyIHVuYSBudWV2YSBiYXNlIGRlIGRhdG9zIGJhc2FkYSBlbiBlc3RlIG1hdGNoaW5nIGNvbiBsYSBmdW5jacOzbiBgbWF0Y2guZGF0YSgpYC4gT2JzZXJ2YSBjw7NtbyBhaG9yYSBoYXkgc8OzbG8gMTExNyBmaWxhcyBlbiBsdWdhciBkZSAxNzUyLCB5YSBxdWUgaGVtb3MgZGVzY2FydGFkbyBsYXMgb2JzZXJ2YWNpb25lcyBubyBlbXBhcmVqYWRhcy4gVGFtYmnDqW4gYWhvcmEgaGF5IHVuYSBudWV2YSBjb2x1bW5hIGxsYW1hZGEgd2VpZ2h0cy4gTGEgZnVuY2nDs24gYG1hdGNoaXQoKWAgYXNpZ25hIGEgbG9zIHBhcmVzIGRlIG9ic2VydmFjaW9uZXMgZW1wYXJlamFkYXMgZGlmZXJlbnRlcyBwZXNvcyBkZXBlbmRpZW5kbyBkZSBsbyBjZXJjYSBvIGxlam9zIHF1ZSBlc3TDqW4gbG9zIGVtcGFyZWphbWllbnRvcyBlbiB1biBpbnRlbnRvIGRlIGNvbnRyb2xhciBsYSB2YXJpYWNpw7NuIGRlIGxhIGRpc3RhbmNpYS4gUG9kZW1vcyB1dGlsaXphciBlc3RvcyBwZXNvcyBlbiBudWVzdHJvIG1vZGVsbyBkZSByZWdyZXNpw7NuIHBhcmEgbWVqb3JhciBudWVzdHJhIGVzdGltYWNpw7NuIGRlbCBlZmVjdG8gY2F1c2FsCgpgYGB7cn0KbW9zcXVpdG9fbmV0c19tYXRjaGVkIDwtIG1hdGNoLmRhdGEobWF0Y2hlZCkgCmdsaW1wc2UobW9zcXVpdG9fbmV0c19tYXRjaGVkKSAKYGBgCgoqKlBvciB1bHRpbW8qKixwb2RlbW9zIGhhY2VyIHVuYSByZWdyZXNpw7NuIHVzYW5kbyBsb3MgZGF0b3MgZW1wYXJlamFkb3M6CgpgYGB7cn0KCiMgSW5kaWNhbW9zIG1vZGVsbyBsaW5lYWwKbW9kZWxfbWF0Y2hlZCA8LSBsbShtYWxhcmlhX3Jpc2sgfiBuZXQsIGRhdGEgPSBtb3NxdWl0b19uZXRzX21hdGNoZWQsCndlaWdodHMgPSB3ZWlnaHRzKQoKIyBWZW1vcyByZXN1bWVuIGVzdGFkaXN0aWNvCnN1bW1hcnkobW9kZWxfbWF0Y2hlZCkKCmBgYAoKU2Vnw7puIGVzdG9zIHJlc3VsdGFkb3MsIGVsIHVzbyBkZSB1biBtb3NxdWl0ZXJvIGNhdXNhIHVuYSBkaXNtaW51Y2nDs24gZGUgMTAuNDggcHVudG9zIGVuIGVsIHJpZXNnbyBkZSBtYWxhcmlhLCBlbiBwcm9tZWRpby4gVW5hIHZleiBtw6FzLCBwb2RlbW9zIHV0aWxpemFyIGVsIGxlbmd1YWplIGNhdXNhbCBhaG9yYSBwb3JxdWUgaGVtb3MgYWp1c3RhZG8gbG9zIGNvbmZ1c29yZXMgYWwgaGFjZXIgbGEgY29tcGFyYWNpw7NuLCBpZGVudGlmaWNhbmRvIGFzw60gZWwgY2FtaW5vIGNhdXNhbCBlbnRyZSBlbCB1c28gZGUgbW9zcXVpdGVyb3MgeSBlbCByaWVzZ28gZGUgbWFsYXJpYS4KCiMgUG9uZGVyYWNpw7NuIGRlIHByb2JhYmlsaWRhZCBpbnZlcnNhCgpVbmEgZGVzdmVudGFqYSBkZWwgdXNvIGRlIG1hdGNoaW5nIGVzIHF1ZSBkZXNlY2hhbW9zIG11Y2hhIGluZm9ybWFjacOzbjogbGFzIG9ic2VydmFjaW9uZXMgbm8gY29pbmNpZGVudGVzIHNlIGRlc2NhcnRhbiwgeSBlbCB0YW1hw7FvIGRlIG51ZXN0cmEgbXVlc3RyYSBwdWVkZSByZWR1Y2lyc2Ugc2lnbmlmaWNhdGl2YW1lbnRlLiBFbCBlbXBhcmVqYW1pZW50byB0YW1iacOpbiB0aWVuZGUgYSBzZXIgbXV5IHJpZ3Vyb3NvLCB5YSBxdWUgY2FkYSBvYnNlcnZhY2nDs24gdHJhdGFkYSBkZWJlIHNlciBlbXBhcmVqYWRhIGNvbiB1bmEgKG8gbcOhcykgb2JzZXJ2YWNpb25lcyBubyB0cmF0YWRhcy4gQXVucXVlIHF1ZSBhbGd1bmFzIG9ic2VydmFjaW9uZXMgbm8gdHJhdGFkYXMgZXN0w6FuIGVuIHJlYWxpZGFkIG11eSBjZXJjYSBkZSBsYXMgb2JzZXJ2YWNpb25lcyB0cmF0YWRhcywgYcO6biBhc8OtIHNlIGRlc2NhcnRhbiBwb3JxdWUgZnVlcm9uIHN1cGVyYWRhcyBwb3Igb2JzZXJ2YWNpb25lcyBxdWUgdGllbmVuIHVuYSBkaXN0YW5jaWEgbGlnZXJhbWVudGUgbWVub3IuCgpFbiBsdWdhciBkZSBkZXNlY2hhciBkYXRvcyBwb3RlbmNpYWxtZW50ZSDDunRpbGVzLCBwb2RlbW9zIHV0aWxpemFyIG90cm9zIG3DqXRvZG9zIHBhcmEgY3JlYXIgY29pbmNpZGVuY2lhcyBxdWUgc2VhbiBtZW5vcyByaWd1cm9zYXMgcGVybyBtw6FzIGluZm9ybWF0aXZhcy4gVW4gbcOpdG9kbyBjb23Dum4gZW4gZXBpZGVtaW9sb2fDrWEgeSBiaW9lc3RhZMOtc3RpY2EgZXMgbGEgcG9uZGVyYWNpw7NuIGRlIHByb2JhYmlsaWRhZCBpbnZlcnNhIChJUFcsIGVuIGluZ2zDqXMpLiBFbiBsYSBJUFcsIGEgY2FkYSBvYnNlcnZhY2nDs24gc2UgbGUgYXNpZ25hIHVuIHBlc28gYmFzYWRvIGVuIGxvIGJpZW4gcXVlIHN1IGFzaWduYWNpw7NuIHJlYWwgYWwgdHJhdGFtaWVudG8gY29pbmNpZGUgY29uIGxhIHByb2JhYmlsaWRhZCBwcmV2aXN0YSBkZWwgdHJhdGFtaWVudG8sIHkgZXNvcyBwZXNvcyBzZSB1dGlsaXphbiBsdWVnbyBlbiB1biBtb2RlbG8gZGUgcmVncmVzacOzbiBwYXJhIGVzdGltYXIgZWwgZWZlY3RvIGNhdXNhbCBkZWwgdHJhdGFtaWVudG8gZW4gZWwgcmVzdWx0YWRvLgoKKipQcmltZXJvKiosIHBvZGVtb3MgYWp1c3RhciBsb3MgZmFjdG9yZXMgZGUgY29uZnVzacOzbiBkZSBudWVzdHJvIG1vc3F1aXRlcm8geSBlbCBlamVtcGxvIGRlIHJpZXNnbyBkZSBtYWxhcmlhIHVzYW5kbyB1bmEgcG9uZGVyYWNpw7NuIGRlIHByb2JhYmlsaWRhZCBpbnZlcnNhLgoKUGFyYSBlbGxvLCB1c2Ftb3MgdW5hIHJlZ3Jlc2nDs24gbG9nw61zdGljYSBwYXJhIHByZWRlY2lyIGxhIHByb3BlbnNpw7NuIGEgdXNhciB1biBtb3NxdWl0ZXJvIHVzYW5kbyBsb3MgaW5ncmVzb3MsIGxhIHRlbXBlcmF0dXJhIHkgbGEgc2FsdWQuIEx1ZWdvIHVzYW1vcyBsb3MgcHVudGFqZXMgZGUgcHJvcGVuc2nDs24gcGFyYSBjYWxjdWxhciBsb3MgcGVzb3MgZGUgcHJvYmFiaWxpZGFkIGludmVyc2EgdXNhbmRvIGVzdGEgZsOzcm11bGE6CgokJApcZnJhY3tcdGV4dHtNb3NxdWl0ZXJvfX17XHRleHR7UHJvcGVuc2nDs259fSArIFxmcmFjezEgLSBcdGV4dHtNb3NxdWl0ZXJvfX17MSAtIFx0ZXh0e1Byb3BlbnNpw7NufX0KJCQKCmBgYHtyfQojIENyZWFtb3MgcmVncmVzaW9uIGxvZ2lzdGljYQptb2RlbF9tb3NxdWl0b19uZXQgPC0gZ2xtKG5ldCB+IGluY29tZSArIHRlbXBlcmF0dXJlICsgaGVhbHRoLAogICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSBtb3NxdWl0b19uZXRzLCBmYW1pbHkgPSBiaW5vbWlhbChsaW5rID0gImxvZ2l0IikpCgoKCiMgQWdyZWdhbW9zIGlwdwptb3NxdWl0b19uZXRzX2lwdyA8LSBtb3NxdWl0b19uZXRzICU+JSAjIEluZGljYW1vcyBlbCBkZgogIG11dGF0ZShwcm9wZW5zaXR5ID0gcHJlZGljdChtb2RlbF9tb3NxdWl0b19uZXQsIHR5cGUgPSAicmVzcG9uc2UiKSwgI0FwbGljYW1vcyBlbCBtb2RlbG8gbG9naXN0aWNvLCBjb24gbGFzIHByb2JhYmlsaWRhZGVzCiAgICAgICAgIGlwdyA9IChuZXRfbnVtIC8gcHJvcGVuc2l0eSkgKyAoMSAtIG5ldF9udW0pIC8gKDEgLSBwcm9wZW5zaXR5KSkgIyBjcmVhbW9zIGNvbHVtbmEgaXB3CgoKIyBWZW1vcyByZXN1bHRhZG9zCnByaW50KG1vc3F1aXRvX25ldHNfaXB3KQpgYGAKCkVzdGUgY8OzZGlnbyBhanVzdGEgdW4gbW9kZWxvIGRlIHJlZ3Jlc2nDs24gbG9nw61zdGljYSBwYXJhIGVzdGltYXIgbGEgcHJvYmFiaWxpZGFkIGRlIHF1ZSB1bmEgcGVyc29uYSByZWNpYmEgdW4gbW9zcXVpdGVybyAoYG5ldGApIGVuIGZ1bmNpw7NuIGRlIHN1cyBpbmdyZXNvcywgdGVtcGVyYXR1cmEgeSBzYWx1ZCwgeSBsdWVnbyB1dGlsaXphIGVzYXMgcHJvYmFiaWxpZGFkZXMgKHByb3BlbnNpb25lcykgcGFyYSBjYWxjdWxhciBwZXNvcyBkZSBwb25kZXJhY2nDs24gcG9yIGxhIGludmVyc2EgZGUgbGEgcHJvYmFiaWxpZGFkIGRlIHRyYXRhbWllbnRvIChJUFcpLiBFc3RvcyBwZXNvcyBwZXJtaXRlbiBjb3JyZWdpciBwb3NpYmxlcyBzZXNnb3MgZGUgc2VsZWNjacOzbiBhbCBjb21wYXJhciBncnVwb3MgdHJhdGFkb3MgeSBubyB0cmF0YWRvcywgYXNpZ25hbmRvIG1heW9yIHBlc28gYSBsYXMgb2JzZXJ2YWNpb25lcyBxdWUgcmVjaWJpZXJvbiB1biB0cmF0YW1pZW50byBpbmVzcGVyYWRvIHNlZ8O6biBzdXMgY2FyYWN0ZXLDrXN0aWNhcy4gQXPDrSwgc2UgYnVzY2Egc2ltdWxhciB1biBlc2NlbmFyaW8gbcOhcyBwYXJlY2lkbyBhIHVuIGV4cGVyaW1lbnRvIGFsZWF0b3JpbyB5IGVzdGltYXIgZGUgZm9ybWEgbcOhcyBqdXN0YSBlbCBlZmVjdG8gZGVsIHRyYXRhbWllbnRvLgoKKipTZWd1bmRvKiosIGFob3JhIHF1ZSB0ZW5lbW9zIHBlc29zIGRlIHByb2JhYmlsaWRhZCBpbnZlcnNhLCBwb2RlbW9zIHVzYXJsb3MgZW4gdW5hIHJlZ3Jlc2nDs246CgpgYGB7cn0KIyBJbmRpY2Ftb3MgbW9kZWxvIGxpbmVhbAptb2RlbF9pcHcgPC0gbG0obWFsYXJpYV9yaXNrIH4gbmV0LCBkYXRhID0gbW9zcXVpdG9fbmV0c19pcHcsCndlaWdodHMgPSBpcHcpCgojIFZlbW9zIHJlc3VsdGFkb3MKc3VtbWFyeShtb2RlbF9pcHcpCgpgYGAKClNlZ8O6biBlc3RlIG1vZGVsbyBkZSBJUFcsIGVsIHVzbyBkZSB1biBtb3NxdWl0ZXJvICpjYXVzYSogdW5hIGRpc21pbnVjacOzbiBkZSAxMC4xMyBwdW50b3MgZW4gZWwgcmllc2dvIGRlIG1hbGFyaWEsIGVuIHByb21lZGlvLiBVbmEgdmV6IG3DoXMsIHBvZGVtb3MgdXRpbGl6YXIgY29uIHNlZ3VyaWRhZCBlbCBsZW5ndWFqZSBjYXVzYWwgcG9ycXVlIGhlbW9zIGlkZW50aWZpY2FkbyBlbCBjYW1pbm8gY2F1c2FsIGVudHJlIGVsIHVzbyBkZSBtb3NxdWl0ZXJvcyB5IGVsIHJpZXNnbyBkZSBtYWxhcmlhIHRlbmllbmRvIGVuIGN1ZW50YSBsb3MgZmFjdG9yZXMgZGUgY29uZnVzacOzbiBlbiBsb3MgcGVzb3MgZGUgcHJvYmFiaWxpZGFkIGludmVyc29zLgoKIyBDb21wYXJhbmRvIHRvZG9zIGxvcyBtw6l0b2RvcwoKQWhvcmEgcXVlIGhlbW9zIGVqZWN1dGFkbyB2YXJpb3MgbW9kZWxvcyBkZSByZWdyZXNpw7NuIHF1ZSBzZSBhanVzdGFuIGEgbG9zIGZhY3RvcmVzIGRlIGNvbmZ1c2nDs24gZGUgZGlmZXJlbnRlcyBtYW5lcmFzLCBwb2RlbW9zIGNvbXBhcmFyIGxvcyByZXN1bHRhZG9zIHRvZG9zIGp1bnRvcy4gTGEgaW5nZW51YSBlc3RpbWFjacOzbiBkZSAtMTYgcGFyZWNlIHNlciBkZWZpbml0aXZhbWVudGUgdW5hIHNvYnJlZXN0aW1hY2nDs246IGRlc3B1w6lzIGRlIGFqdXN0YXIgY29uIGxhIHJlZ3Jlc2nDs24sIGVsIG1hdGNoaW5nIHkgbGEgcG9uZGVyYWNpw7NuIGRlIHByb2JhYmlsaWRhZCBpbnZlcnNhLCBlbCBlZmVjdG8gY2F1c2FsIGRlbCB1c28gZGUgdW4gbW9zcXVpdGVybyBlbiBlbCByaWVzZ28gZGUgbWFsYXJpYSBlcyBjb25zaXN0ZW50ZW1lbnRlIGRlIGFscmVkZWRvciBkZSAtMTAuIEFzdW1pZW5kbyBxdWUgbnVlc3RybyBEQUcgZXN0w6EgY29ycmVjdG8sIGVuY29udHJhbW9zIGNvbiDDqXhpdG8gdW4gZWZlY3RvIGNhdXNhbCBhIHBhcnRpciBkZSBkYXRvcyBubyBleHBlcmltZW50YWxlcyB5IG9ic2VydmFjaW9uYWxlcy4KCmBgYHtyfQp0ZXhyZWc6OnNjcmVlbnJlZyhsaXN0KG1vZGVsX25haXZlLCBtb2RlbF9yZWdyZXNzaW9uLAogICAgICAgICAgICAgICAgICAgICAgIG1vZGVsX21hdGNoZWQsIG1vZGVsX2lwdyksCiAgICAgICAgICAgICAgICAgIGN1c3RvbS5tb2RlbC5uYW1lcyA9IGMoIk5haXZlIiwgIlJlZ3Jlc3Npb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJNYXRjaGluZyIsICJJUFciKSkKCmBgYAo=