Para el modelado de control sintético utilizarémos dos métodos con
dos paqueterias distintas el primero Synth de Jens
Hainmueller and Alexis Diamond de las univesidades Stanford y Harvard,
respectivamente. La documentación al respecto se encuentra aqui
Paquetería Synth
Lo primero será instalar la librería:
library(Synth)
Una vez cargada la librería podremos acceder a la base de datos panel
mediante el siguiente comando:
data("synth.data")
que es un data frame con datos por año desde 1980 hasta el año 2000,
tanto para el grupo de control (\(\textit{donor pool}\)), como para la unidad
de tratamiento (treated.region).
La organización de la base de datos anterior está determinada de la
siguiente manera:
unit.num es el identificador de cada unidad que se
compone de un vector de 8 entradas sin repetición, es decir, \(j=\{2, 7, 13, 17, 29, 32, 36, 38\}\),
siendo la unidad de tratamiento la unidad \(j=7\),
year es un vector numérico con los identificadores
de cada año para \(t=1980,1981,...,2000\) para cada
unidad,
name contiene el nombre de las regiones,
Y es la serie para el \(\textit{outcome}\) de interés,
las columnas X1,X2 y X3
son los predictores de Y.
Vamos a tomar los valores medios de cada predictor para todos los
años, es decir:
\[\frac{1}{T}\sum_{t=1}^{T}x_{ijt}\] es la
entrada de la fila \(i\) y la columna
\(j\) de la matriz \(X_{0}\), que es la matriz de predictores
para las unidades \(j=1,2,...,J\) e
\(i=1,2,3\)
En este ejemplo la matriz de predictores \(X_{1}\) está determinada como sigue:
X1 <- filter(synth.data, name == "treated.region") %>%
select("X1","X2","X3")
X1.1 <- matrix(rep(0,3),ncol=3)
for (i in 1:ncol(X1.1)){
X1.1[,i]<-mean(X1[,i],na.rm = T)
}
X1.1
[,1] [,2] [,3]
[1,] 0.267079 15.61667 21.68571
mientras que la matriz de predictores con las mismas variables para
\(j=2,...,8\), \(X_{0}\), la construimos de la siguiente
manera:
X2 <- filter(synth.data, name == "control.region.northeast") %>%
select("X1","X2","X3")
X2.1 <- matrix(rep(0,3),ncol=3)
for (i in 1:ncol(X2.1)){
X2.1[,i]<-mean(X2[,i],na.rm = T)
}
colnames(X2.1) <- colnames(X2) #Estas son las matrices con los valores medios por columna
X3 <- filter(synth.data, name == "control.region.southcentral") %>%
select("X1","X2","X3")
X3.1 <- matrix(rep(0,3),ncol=3)
for (i in 1:ncol(X3.1)){
X3.1[,i]<-mean(X3[,i],na.rm = T)
}
X4 <- filter(synth.data, name == "control.region.west") %>%
select("X1","X2","X3")
X4.1 <- matrix(rep(0,3),ncol=3)
for (i in 1:ncol(X4.1)){
X4.1[,i]<-mean(X4[,i],na.rm = T)
}
X5 <- filter(synth.data, name == "control.region.southeast") %>%
select("X1","X2","X3")
X5.1 <- matrix(rep(0,3),ncol=3)
for (i in 1:ncol(X5.1)){
X5.1[,i]<-mean(X5[,i],na.rm = T)
}
X6 <- filter(synth.data, name == "control.region.central") %>%
select("X1","X2","X3")
X6.1 <- matrix(rep(0,3),ncol=3)
for (i in 1:ncol(X6.1)){
X6.1[,i]<-mean(X6[,i],na.rm = T)
}
X7 <- filter(synth.data, name == "control.region.south") %>%
select("X1","X2","X3")
X7.1 <- matrix(rep(0,3),ncol=3)
for (i in 1:ncol(X7.1)){
X7.1[,i]<-mean(X7[,i],na.rm = T)
}
X8 <- filter(synth.data, name == "control.region.east") %>%
select("X1","X2","X3")
X8.1 <- matrix(rep(0,3),ncol=3)
for (i in 1:ncol(X8.1)){
X8.1[,i]<-mean(X8[,i],na.rm = T)
}
X0 <- rbind(X2.1,X3.1,X4.1,X5.1,X6.1,X7.1,X8.1)
X0
X1 X2 X3
[1,] 0.2420620 20.122222 19.30000
[2,] 0.2579195 17.905555 19.12143
[3,] 0.2614878 23.894445 22.72143
[4,] 0.2657048 10.650000 23.95000
[5,] 0.2551758 17.811111 21.16429
[6,] 0.2702358 10.900000 22.41429
[7,] 0.2588663 9.822222 29.91429
Ahora vamos a preparar de manera correcta los datos utilizando la
función dataprep:
dataprep.out <- dataprep(foo = synth.data, #Cargar los datos
predictors = c("X1", "X2", "X3"), #Matriz de predictores
predictors.op = "mean", #El operador que se utilizará en los predictores
dependent = "Y", #Outcome
unit.variable = "unit.num",
time.variable = "year", #Frecuencia temporal de la base
special.predictors = list( #Predictores numéricos adicionales
list("Y", 1991, "mean"), #que asocia periodos previos al
list("Y", 1985, "mean"), #tratamiento y operadores "mean".
list("Y", 1980, "mean")),
treatment.identifier = 7, #id de la unidad de tratamiento
controls.identifier = c(2, 13, 17,
29, 32, 36, 38), #id de las unidades de control
time.predictors.prior = c(1980:1989), #Un vector numérico que contiene los periodos previos al tratamiento sobre los que el predictor será promediado.
time.optimize.ssr = c(1980:1989), #Un vector numérico que identifica los periodos de la variable dependiente sobre los cuales la función de perdida se minimiza (i.e., la minimización de la suma de los residuos al cuadrado entre la unidad de tratamiento y la unidad de control.)
unit.names.variable = "name", #Nombre de las unidades de control
time.plot = 1980:1996) #Un vector que identifica los períodos durante los cuales se trazarán los resultados con gaps.plot y path.plot.
Missing data- treated unit; predictor: X1 ; for period: 1980
We ignore (na.rm = TRUE) all missing values for predictors.op.
Missing data- treated unit; predictor: X3 ; for period: 1980
We ignore (na.rm = TRUE) all missing values for predictors.op.
Missing data- treated unit; predictor: X3 ; for period: 1981
We ignore (na.rm = TRUE) all missing values for predictors.op.
Missing data- treated unit; predictor: X3 ; for period: 1982
We ignore (na.rm = TRUE) all missing values for predictors.op.
Missing data- treated unit; predictor: X3 ; for period: 1983
We ignore (na.rm = TRUE) all missing values for predictors.op.
Missing data - control unit: 2 ; predictor: X1 ; for period: 1980
We ignore (na.rm = TRUE) all missing values for predictors.op.
Missing data - control unit: 2 ; predictor: X3 ; for period: 1980
We ignore (na.rm = TRUE) all missing values for predictors.op.
Missing data - control unit: 2 ; predictor: X3 ; for period: 1981
We ignore (na.rm = TRUE) all missing values for predictors.op.
Missing data - control unit: 2 ; predictor: X3 ; for period: 1982
We ignore (na.rm = TRUE) all missing values for predictors.op.
Note que para generar las matrices \(X_{0}\) tomamos los promedios por año de
las observaciones para cada variable \(X_{j}\) con \(j=1,2,3\) y las variables especiales son
simplemente las observaciones para \(Y\) de los años 1980, 1985 y 1990.
Para generar el control sintetico, es decir el vector de ponderadores
\(W=(\omega_{2},...,\omega_{J+1})^{T}\)
tal que
\[Y_{1t}^{N}=\sum_{j=2}^{J+1} \omega_{j}
Y_{jt}\]
donde \(Y_{1t}^{N}\) es el outcome
de interés en ausencia de la intevención, para \(t>T_{0}\), el periodo en el que ocurre
la intervención.
synth.out <- synth(dataprep.out) #Genera el vector de 6x1 entradas del control sintetico para cada predictor
X1, X0, Z1, Z0 all come directly from dataprep object.
****************
searching for synthetic control unit
****************
****************
****************
MSPE (LOSS V): 3.320744
solution.v:
1.231e-07 0.001232258 0.003208833 0.3528442 0.005254982 0.6374596
solution.w:
0.104716 0.005893509 0.03580648 0.06678495 0.5510233 0.1552163 0.08055942
round(synth.out$solution.w,2) #Redondea y presenta las variables a dos caracteres después del punto decimal
w.weight
2 0.10
13 0.01
17 0.04
29 0.07
32 0.55
36 0.16
38 0.08
gaps<- dataprep.out$Y1plot-(
dataprep.out$Y0plot%*%synth.out$solution.w) #Esta ecuación es simplemente el tau_1t para todos los periodos.
Como habíamos convenido en la entrada de teoría de control sintético
uno de los requerimientos para valorar el vector \(W^{*}\) es la regla del convex
hull, es decir
\[X_{1}-X_{0}W^{*}\approx 0\]
t(X1.1)-(t(X0)%*%synth.out$solution.w)
w.weight
X1 0.009696174
X2 -0.460281992
X3 -0.412026145
De acuerdo con la teoría, para obtener mayor aproximación tendriamos
que utilizar más información para antes del periodo de intervención.
Finalmente, para presentar de manera gráfica los datos utilizamos las
funciones path.plot() para graficar las trayectorias
observadas y las sintéticas, y gaps.plot() para gráficar
las diferencias entre la serie observada y la sintética en todo el
horizonte temoporal.
synth.tables <- synth.tab(
dataprep.res = dataprep.out,
synth.res = synth.out) #Presenta los resultados del modelo en una tabla
print(synth.tables)
$tab.pred
Treated Synthetic Sample Mean
X1 0.267 0.257 0.259
X2 16.140 17.079 16.554
X3 21.717 22.046 22.795
special.Y.1991 115.000 115.016 117.371
special.Y.1985 128.800 127.715 132.457
special.Y.1980 134.000 134.028 145.757
$tab.v
v.weights
X1 0
X2 0.001
X3 0.003
special.Y.1991 0.353
special.Y.1985 0.005
special.Y.1980 0.637
$tab.w
w.weights unit.names unit.numbers
2 0.105 control.region.northeast 2
13 0.006 control.region.southcentral 13
17 0.036 control.region.west 17
29 0.067 control.region.southeast 29
32 0.551 control.region.central 32
36 0.155 control.region.south 36
38 0.081 control.region.east 38
$tab.loss
Loss W Loss V
[1,] 6.647013e-05 3.320744
path.plot(dataprep.res = dataprep.out,
synth.res = synth.out,
Main = "Trayectorias de control sintético") #Grafica las trayectorias de la unidad tratada y su trayectoria sintética
abline(v = 1991, col = "red", lty = 2)
text(1991, 100,"Periodo de \n Tratamiento (T0) ",
pos = 2, col ="red")

gaps.plot(dataprep.res = dataprep.out,
synth.res = synth.out,
Main = "Brechas del cotrol sintético") #Grafica las discrepancias entre la unidad tratada y su trayectoria sintética
abline(v = 1991, col = "red", lty = 2)
text(1991, 20, "Periodo de \n Tratamiento (T0) ",
pos = 2, col ="red")

Paquetería tidysynth
Ahora vamos a trabajar con la libreria tidysynth, lo
pimero que hay que hacer es instalar la libreria:
library(tidysynth)
La base de datos que vamos a utilizar proviene de Abadie et
al. (2010). La base de datos se compone de la siguiente manera:
La columna state de clase “character”
contiene los nombres de los distintos estados de Estados
Unidos,
la columna numeric de clase “numeric”
contiene el año en que se realizan las observaciones desde el año 1970
al año 2000,
la columna cigsale de clase “numeric”
contiene el total de la venta de cigarrillos per-capita por
estado,
la columna lnincome de clase “numeric”
contiene el logaritmo del ingreso personal de cada estado
la columna beer de clase “numeric” contiene
el consumo de cerveza per-capita
la columna age15to24 de clase “numeric”
contiene contiene el porcentaje de la población entre 15 y 24
años
la columna retprice de clase “numeric”
contiene el precio promedio de retail de los
cigarrillos.
En este ejemplo, el outcome de interés es la venta de
cigarrillos per-capita, y la unidad de tratamiento es el estado de
California, mientras que las unidades de conrrol serán el resto de los
estaods de Estados Unidos. Lo que se intenta estimar es la trayectoria
sintética de la venta de cigarrillos en California de no haberse
aplicado el tratamiento, que es, la llamada Proposition 99 que
consistió en un aumento de 25 centavos por paquete a través de un
impuesto.
data("smoking")
Para preparar los datos como en la paquetería Synth
utilizamos la funciónm synthetic_control() de la manera
siguiente, la matriz predictores \(X_{0}\) y \(X_{1}\) con la función
generate_predictors(), y generar los ponderadores con la
función generate_controls(). Dado que la paqueteria es una
implementación tidy la concatenación de las funciones se
hace a través del comando %>%, como se muestra a
continuación:
smoking_out <-
smoking %>% #Este es el data frame que vamos a utilizar
synthetic_control(outcome = cigsale, #El outcome de interés
unit = state, #Vamos a utilizar la columna state como indice de las unidades
time = year, #Con indice de tiempo de la columna year
i_unit = "California", #La unidad de tratamiento j=1
i_time = 1988, #El periodo de tratamiento T0
generate_placebos=T #Generaremos placebos para hacer inferencia.
) %>%
# Vamos a generar los predictores agregados para ajustar los ponderadores:
# promedio del logaritmo del ingreso, el precio de los cigarrillos y la
# prporcion de la población entre 15 y 24 años de 1980 - 1988
generate_predictor(time_window = 1980:1988, # Los periodos hasta T0=1988
ln_income = mean(lnincome, na.rm = T), #log del ingreso
ret_price = mean(retprice, na.rm = T), #el precio de los cigarrillos
youth = mean(age15to24, na.rm = T)) %>% #proporcion de la pob entre 15 - 24 años
# promedio del consumo de cerveza en el donnor pool de 1984 - 1988
generate_predictor(time_window = 1984:1988,
beer_sales = mean(beer, na.rm = T)) %>%
# las ventas de cigarrillos atrasadas
generate_predictor(time_window = 1975,
cigsale_1975 = cigsale) %>%
generate_predictor(time_window = 1980,
cigsale_1980 = cigsale) %>%
generate_predictor(time_window = 1988,
cigsale_1988 = cigsale) %>%
# genera los ponderadores
generate_weights(optimization_window = 1970:1988, # el periodo para optimización de los ponderadores
margin_ipop = .02, sigf_ipop = 7,
bound_ipop = 6 # opciones de optimización
) %>%
# Generar el control sintético
generate_control()
Para graficar las trayectorias de las series observadas y la serie
sintética del problema anterior utilizamos el siguiente comando:
smoking_out %>% plot_trends()

Además, podemos graficar las diferencias absolutas entre la serie
observada y la serie sintética:
smoking_out %>% plot_differences()

Los siguientes histogramas muestral la ponderación que el programa le
da a las diferentes unidades, siendo Utah el que recibe mayor
ponderación y Conecticut el que menos pero estrictamente positivo.
Además, del lado derecho se muestran las ponderaciones para las
diferntes variables en la matriz de predictores para todas las unidades
\([X_{0}:X_{1}]\)
Recoremos que tanto este método como el de la paquetería
Synth generan los pesos bajo las siguientes
condiciones:
\[\sum_{j=2}^{J+1}\omega_{j}=1\] y
\[\omega_{j}\geq 0 \quad \text{con} \quad
j=2,...,J+1\]
smoking_out %>% plot_weights()

De acuerdo con Abadie et. al., (2010) el método de control sintético
produce ponderadores iguales a cero para la mayoría de las unidades del
donor pool.
smoking_out %>% grab_balance_table()
Inferencia
En cuanto a la inferencia, el método se basa en repetir el método
para cada donante en el grupo de donantes exactamente de la misma manera
que se hizo para la unidad tratada, es decir, generando controles
sintéticos de placebo. Al establecer
generate_placebos = TRUE al inicializar la tubería ‘synth’
con synthetic_control(), los casos de placebo se generan
automáticamente al construir el control sintético de interés. Esto
facilita la exploración de cuán única es la diferencia entre la unidad
observada y la unidad sintética en comparación con los placebos
smoking_out %>%
plot_placebos()

Ten en cuenta que la función plot_placebos()
automáticamente elimina cualquier placebo que se ajuste mal a los datos
en el período previo a la intervención. La razón para hacerlo es
puramente visual: esas unidades tienden a distorsionar la escala al
trazar los placebos. Para realizar la eliminación, la función examina el
error cuadrático medio de predicción (MSPE) en el período previo a la
intervención (es decir, una métrica que refleja cuán bien se ajusta el
control sintético a la serie de tiempo de resultados observados en el
período previo a la intervención). Si un control de placebo tiene un
MSPE que es dos veces mayor que el caso objetivo (por ejemplo,
“California”), entonces se elimina. Para desactivar este comportamiento,
establece prune = FALSE.
smoking_out %>% plot_placebos(prune = FALSE)

Finalmente, Adabie et al. en 2010 describen una manera de construir
los valores p exactos de Fisher dividiendo el MSPE posterior a la
intervención por el MSPE anterior a la intervención y luego clasificando
todos los casos en orden descendente según esta proporción. Se construye
un valor p tomando la clasificación entre el total de casos. La idea
subyacente es que si el control sintético se ajusta bien a la serie de
tiempo observada (bajo MSPE en el período previo) y se desvía en el
período posterior (alto MSPE en el período posterior), entonces hay un
efecto significativo debido a la intervención. Si la intervención no
tuvo efecto, entonces el período posterior y el período previo deberían
continuar mapeándose bastante bien, lo que daría como resultado una
proporción cercana a 1. Si las unidades de placebo se ajustan de manera
similar a los datos, entonces no podemos rechazar la hipótesis nula de
que la intervención no tuvo efecto.
Esta proporción se puede representar fácilmente utilizando
plot_mspe_ratio(), lo que ofrece información sobre la
rareza del caso en el que la intervención realmente ocurrió.
smoking_out %>% plot_mspe_ratio()

Un segundo ejemplo:
library(Synth)
data("synth.data")
synth.data <- filter(synth.data,
year <= 1996)
synth.data.out <-
synth.data %>%
synthetic_control(outcome = Y,
unit = name,
time = year,
i_unit = "treated.region",
i_time = 1991,
generate_placebos = T
) %>%
generate_predictor(time_window = 1980:1991,
x1 = mean(X1, na.rm = T),
x2 = mean(X2, na.rm = T),
x3 = mean(X3, na.rm = T)
) %>%
generate_predictor(time_window = 1991,
Y_91 = Y) %>%
generate_predictor(time_window = 1985,
Y_85 = Y) %>%
generate_predictor(time_window = 1980,
Y_80 = Y) %>%
generate_weights(optimization_window = 1970:1991,
margin_ipop = .02, sigf_ipop = 7,
bound_ipop = 6
) %>%
generate_control()
synth.data.out %>%
plot_trends()

synth.data.out %>%
plot_differences()

synth.data.out %>%
plot_weights()

synth.data.out %>%
grab_balance_table()
Cuidate bb
LS0tCnRpdGxlOiAiQ29udHJvbCBTaW50w6l0aWNvIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgo8YnI+PGJyPjxicj4KCjxwIHN0eWxlPSJ0ZXh0LWFsaWduOmNlbnRlcjsiPgoqUXVpZXJvIHF1ZSBxdWVkZSBjbGFybyBxdWUgbm8gdGVuZ28gaWRlYSBkZSBsbyBxdWUgZXN0b3kgYXB1bnRvIGRlIGVzY3JpYmlyLCDDum5pY2FtZW50ZSBlcyB1bmEgY29tcGlsYWNpw7NuIGRlIGRvY3VtZW50b3MgcXVlIGhlIGVuY29udHJhZG8gc29icmUgbW9kZWxhZG8gZGUgY29udHJvbCBzaW50w6l0aWNvLiBQb3IgbG8gdGFudG8sIGRlc2N1YnJpcmVtb3MganVudG9zIHkgYWwgbWlzbW8gdGllbXBvIChlc28gbm8gZXMgdmVyZGFkKSBxdWUgaGFjZSBjYWRhIHVuYSBkZSBsYXMgc2lndWllbnRlcyBsaW5lYXMuIFBvciBmYXZvciBubyBqdXpndWVzLCB0dXZlIGxhIGRlY2VuY2lhIGRlIGFkdmVydGlydGUuKgo8L3A+Cgo8cCBzdHlsZT0idGV4dC1hbGlnbjpjZW50ZXI7Ij4KLUMKPC9wPgoKPGJyPjxicj48YnI+PGJyPjxicj48YnI+CgpQYXJhIGVsIG1vZGVsYWRvIGRlIGNvbnRyb2wgc2ludMOpdGljbyB1dGlsaXphcsOpbW9zIGRvcyBtw6l0b2RvcyBjb24gZG9zIHBhcXVldGVyaWFzIGRpc3RpbnRhcyBlbCBwcmltZXJvIApgU3ludGhgIGRlIEplbnMgSGFpbm11ZWxsZXIgYW5kIEFsZXhpcyBEaWFtb25kIGRlIGxhcyB1bml2ZXNpZGFkZXMgU3RhbmZvcmQgeSBIYXJ2YXJkLCByZXNwZWN0aXZhbWVudGUuIExhIGRvY3VtZW50YWNpw7NuIGFsIHJlc3BlY3RvIHNlIGVuY3VlbnRyYSBbYXF1aV0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL1N5bnRoL1N5bnRoLnBkZi4gKSAKCkxhIHNlZ3VuZGEgcGFxdWV0ZXLDrWEsIGB0aWR5c3ludGhgIHF1ZSBlc3RhIGNvbnN0cnVpZGEgc29icmUgbGEgcGFxdWV0ZXLDrWEgYW50ZXJpb3IuIEhhc3RhIGRvbmRlIGVudGllbmRvLCBsYSBjcmVhY2nDs24gZGUgZXN0YSBwYXF1ZXRlcmlhIGxlIGNvcnJlc3BvbmRlIGEgRXJpYyBEdW5mb3JkIGRlIE1ldGEgeSBwdWVkZXMgY29uc3VsdGFyIGVsIGByZWFkbWVgIGNvbXBsZXRvIFthcXVpXShodHRwczovL2dpdGh1Yi5jb20vZWR1bmZvcmQvdGlkeXN5bnRoI3JlYWRtZSkKCjxicj48YnI+PGJyPgoKIyMjIFBhcXVldGVyw61hIGBTeW50aGAKCkxvIHByaW1lcm8gc2Vyw6EgaW5zdGFsYXIgbGEgbGlicmVyw61hOgpgYGB7cn0KbGlicmFyeShTeW50aCkgCmBgYAoKVW5hIHZleiBjYXJnYWRhIGxhIGxpYnJlcsOtYSBwb2RyZW1vcyBhY2NlZGVyIGEgbGEgYmFzZSBkZSBkYXRvcyBwYW5lbCBtZWRpYW50ZSBlbCBzaWd1aWVudGUgY29tYW5kbzoKYGBge3J9CmRhdGEoInN5bnRoLmRhdGEiKQpgYGAKcXVlIGVzIHVuIGRhdGEgZnJhbWUgY29uIGRhdG9zIHBvciBhw7FvIGRlc2RlIDE5ODAgaGFzdGEgZWwgYcOxbyAyMDAwLCB0YW50byBwYXJhIGVsIGdydXBvIGRlIGNvbnRyb2wgKCRcdGV4dGl0e2Rvbm9yIHBvb2x9JCksIGNvbW8gcGFyYSBsYSB1bmlkYWQgZGUgdHJhdGFtaWVudG8gKGB0cmVhdGVkLnJlZ2lvbmApLgoKTGEgb3JnYW5pemFjacOzbiBkZSBsYSBiYXNlIGRlIGRhdG9zIGFudGVyaW9yIGVzdMOhIGRldGVybWluYWRhIGRlIGxhIHNpZ3VpZW50ZSBtYW5lcmE6CgoqIGB1bml0Lm51bWAgZXMgZWwgaWRlbnRpZmljYWRvciBkZSBjYWRhIHVuaWRhZCBxdWUgc2UgY29tcG9uZSBkZSB1biB2ZWN0b3IgZGUgOCBlbnRyYWRhcyBzaW4gcmVwZXRpY2nDs24sIGVzIGRlY2lyLCAkaj1cezIsICA3LCAxMywgMTcsIDI5LCAzMiwgMzYsIDM4XH0kLCBzaWVuZG8gbGEgdW5pZGFkIGRlIHRyYXRhbWllbnRvIGxhIHVuaWRhZCAkaj03JCwKCiogYHllYXJgIGVzIHVuIHZlY3RvciBudW3DqXJpY28gY29uIGxvcyBpZGVudGlmaWNhZG9yZXMgZGUgY2FkYSBhw7FvIHBhcmEgJHQ9MTk4MCwxOTgxLC4uLiwyMDAwJCBwYXJhIGNhZGEgdW5pZGFkLAoKKiBgbmFtZWAgY29udGllbmUgZWwgbm9tYnJlIGRlIGxhcyByZWdpb25lcywKCiogYFlgIGVzIGxhIHNlcmllIHBhcmEgZWwgJFx0ZXh0aXR7b3V0Y29tZX0kIGRlIGludGVyw6lzLAoKKiBsYXMgY29sdW1uYXMgYFgxYCxgWDJgIHkgYFgzYCBzb24gbG9zIHByZWRpY3RvcmVzIGRlIGBZYC4KCgoKVmFtb3MgYSB0b21hciBsb3MgdmFsb3JlcyBtZWRpb3MgZGUgY2FkYSBwcmVkaWN0b3IgcGFyYSB0b2RvcyBsb3MgYcOxb3MsIGVzIGRlY2lyOgoKJCRcZnJhY3sxfXtUfVxzdW1fe3Q9MX1ee1R9eF97aWp0fSQkCmVzIGxhIGVudHJhZGEgZGUgbGEgZmlsYSAkaSQgeSBsYSBjb2x1bW5hICRqJCBkZSBsYSBtYXRyaXogJFhfezB9JCwgcXVlIGVzIGxhIG1hdHJpeiBkZSBwcmVkaWN0b3JlcyBwYXJhIGxhcyB1bmlkYWRlcyAkaj0xLDIsLi4uLEokIGUgJGk9MSwyLDMkCgoKRW4gZXN0ZSBlamVtcGxvIGxhIG1hdHJpeiBkZSBwcmVkaWN0b3JlcyAkWF97MX0kIGVzdMOhIGRldGVybWluYWRhIGNvbW8gc2lndWU6CmBgYHtyfQpYMSA8LSBmaWx0ZXIoc3ludGguZGF0YSwgbmFtZSA9PSAidHJlYXRlZC5yZWdpb24iKSAlPiUgCiAgc2VsZWN0KCJYMSIsIlgyIiwiWDMiKQpYMS4xIDwtIG1hdHJpeChyZXAoMCwzKSxuY29sPTMpIApmb3IgKGkgaW4gIDE6bmNvbChYMS4xKSl7CiAgWDEuMVssaV08LW1lYW4oWDFbLGldLG5hLnJtID0gVCkKfQoKWDEuMQpgYGAKbWllbnRyYXMgcXVlIGxhIG1hdHJpeiBkZSBwcmVkaWN0b3JlcyBjb24gbGFzIG1pc21hcyB2YXJpYWJsZXMgcGFyYSAkaj0yLC4uLiw4JCwgJFhfezB9JCwgbGEgY29uc3RydWltb3MgZGUgbGEgc2lndWllbnRlIG1hbmVyYToKCmBgYHtyfQpYMiA8LSBmaWx0ZXIoc3ludGguZGF0YSwgbmFtZSA9PSAiY29udHJvbC5yZWdpb24ubm9ydGhlYXN0IikgJT4lIAogIHNlbGVjdCgiWDEiLCJYMiIsIlgzIikgICAgICAgICAgICAgIApYMi4xIDwtIG1hdHJpeChyZXAoMCwzKSxuY29sPTMpIApmb3IgKGkgaW4gIDE6bmNvbChYMi4xKSl7CiAgWDIuMVssaV08LW1lYW4oWDJbLGldLG5hLnJtID0gVCkKfQpjb2xuYW1lcyhYMi4xKSA8LSBjb2xuYW1lcyhYMikgICAgICAgICAgICAgICAgICAgICNFc3RhcyBzb24gbGFzIG1hdHJpY2VzIGNvbiBsb3MgdmFsb3JlcyBtZWRpb3MgcG9yIGNvbHVtbmEKClgzIDwtIGZpbHRlcihzeW50aC5kYXRhLCBuYW1lID09ICJjb250cm9sLnJlZ2lvbi5zb3V0aGNlbnRyYWwiKSAlPiUgCiAgc2VsZWN0KCJYMSIsIlgyIiwiWDMiKQpYMy4xIDwtIG1hdHJpeChyZXAoMCwzKSxuY29sPTMpIApmb3IgKGkgaW4gIDE6bmNvbChYMy4xKSl7CiAgWDMuMVssaV08LW1lYW4oWDNbLGldLG5hLnJtID0gVCkKfQoKClg0IDwtIGZpbHRlcihzeW50aC5kYXRhLCBuYW1lID09ICJjb250cm9sLnJlZ2lvbi53ZXN0IikgJT4lIAogIHNlbGVjdCgiWDEiLCJYMiIsIlgzIikKWDQuMSA8LSBtYXRyaXgocmVwKDAsMyksbmNvbD0zKSAKZm9yIChpIGluICAxOm5jb2woWDQuMSkpewogIFg0LjFbLGldPC1tZWFuKFg0WyxpXSxuYS5ybSA9IFQpCn0KCgpYNSA8LSBmaWx0ZXIoc3ludGguZGF0YSwgbmFtZSA9PSAiY29udHJvbC5yZWdpb24uc291dGhlYXN0IikgJT4lIAogIHNlbGVjdCgiWDEiLCJYMiIsIlgzIikKWDUuMSA8LSBtYXRyaXgocmVwKDAsMyksbmNvbD0zKSAKZm9yIChpIGluICAxOm5jb2woWDUuMSkpewogIFg1LjFbLGldPC1tZWFuKFg1WyxpXSxuYS5ybSA9IFQpCn0KCgpYNiA8LSBmaWx0ZXIoc3ludGguZGF0YSwgbmFtZSA9PSAiY29udHJvbC5yZWdpb24uY2VudHJhbCIpICU+JSAKICBzZWxlY3QoIlgxIiwiWDIiLCJYMyIpClg2LjEgPC0gbWF0cml4KHJlcCgwLDMpLG5jb2w9MykgCmZvciAoaSBpbiAgMTpuY29sKFg2LjEpKXsKICBYNi4xWyxpXTwtbWVhbihYNlssaV0sbmEucm0gPSBUKQp9CgoKWDcgPC0gZmlsdGVyKHN5bnRoLmRhdGEsIG5hbWUgPT0gImNvbnRyb2wucmVnaW9uLnNvdXRoIikgJT4lIAogIHNlbGVjdCgiWDEiLCJYMiIsIlgzIikKWDcuMSA8LSBtYXRyaXgocmVwKDAsMyksbmNvbD0zKSAKZm9yIChpIGluICAxOm5jb2woWDcuMSkpewogIFg3LjFbLGldPC1tZWFuKFg3WyxpXSxuYS5ybSA9IFQpCn0KCgpYOCA8LSBmaWx0ZXIoc3ludGguZGF0YSwgbmFtZSA9PSAiY29udHJvbC5yZWdpb24uZWFzdCIpICU+JSAKICBzZWxlY3QoIlgxIiwiWDIiLCJYMyIpClg4LjEgPC0gbWF0cml4KHJlcCgwLDMpLG5jb2w9MykgCmZvciAoaSBpbiAgMTpuY29sKFg4LjEpKXsKICBYOC4xWyxpXTwtbWVhbihYOFssaV0sbmEucm0gPSBUKQp9CgoKWDAgPC0gcmJpbmQoWDIuMSxYMy4xLFg0LjEsWDUuMSxYNi4xLFg3LjEsWDguMSkKWDAKYGBgCgoKQWhvcmEgdmFtb3MgYSBwcmVwYXJhciBkZSBtYW5lcmEgY29ycmVjdGEgbG9zIGRhdG9zIHV0aWxpemFuZG8gbGEgZnVuY2nDs24gYGRhdGFwcmVwYDoKCmBgYHtyfQpkYXRhcHJlcC5vdXQgPC0gZGF0YXByZXAoZm9vID0gc3ludGguZGF0YSwgICAgICAgICAgICAgICAgICAgICAgICAjQ2FyZ2FyIGxvcyBkYXRvcwogICAgICAgICAgICAgICAgICAgICAgICAgcHJlZGljdG9ycyA9IGMoIlgxIiwgIlgyIiwgIlgzIiksICAgICAgICAjTWF0cml6IGRlIHByZWRpY3RvcmVzIAogICAgICAgICAgICAgICAgICAgICAgICAgcHJlZGljdG9ycy5vcCA9ICJtZWFuIiwgICAgICAgICAgICAgICAgICAjRWwgb3BlcmFkb3IgcXVlIHNlIHV0aWxpemFyw6EgZW4gbG9zIHByZWRpY3RvcmVzIAogICAgICAgICAgICAgICAgICAgICAgICAgZGVwZW5kZW50ID0gIlkiLCAgICAgICAgICAgICAgICAgICAgICAgICAjT3V0Y29tZQogICAgICAgICAgICAgICAgICAgICAgICAgdW5pdC52YXJpYWJsZSA9ICJ1bml0Lm51bSIsICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgIHRpbWUudmFyaWFibGUgPSAieWVhciIsICAgICAgICAgICAgICAgICAgI0ZyZWN1ZW5jaWEgdGVtcG9yYWwgZGUgbGEgYmFzZQogICAgICAgICAgICAgICAgICAgICAgICAgc3BlY2lhbC5wcmVkaWN0b3JzID0gbGlzdCggICAgICAgICAgICAgICAjUHJlZGljdG9yZXMgbnVtw6lyaWNvcyBhZGljaW9uYWxlcwogICAgICAgICAgICAgICAgICAgICAgICAgICBsaXN0KCJZIiwgMTk5MSwgIm1lYW4iKSwgICAgICAgICAgICAgICAjcXVlIGFzb2NpYSBwZXJpb2RvcyBwcmV2aW9zIGFsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpc3QoIlkiLCAxOTg1LCAibWVhbiIpLCAgICAgICAgICAgICAgICN0cmF0YW1pZW50byB5IG9wZXJhZG9yZXMgIm1lYW4iLgogICAgICAgICAgICAgICAgICAgICAgICAgICBsaXN0KCJZIiwgMTk4MCwgIm1lYW4iKSksCiAgICAgICAgICAgICAgICAgICAgICAgICB0cmVhdG1lbnQuaWRlbnRpZmllciA9IDcsICAgICAgICAgICAgICAgICNpZCBkZSBsYSB1bmlkYWQgZGUgdHJhdGFtaWVudG8KICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnRyb2xzLmlkZW50aWZpZXIgPSBjKDIsIDEzLCAxNywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAyOSwgMzIsIDM2LCAzOCksICAjaWQgZGUgbGFzIHVuaWRhZGVzIGRlIGNvbnRyb2wKICAgICAgICAgICAgICAgICAgICAgICAgIHRpbWUucHJlZGljdG9ycy5wcmlvciA9IGMoMTk4MDoxOTg5KSwgICAgI1VuIHZlY3RvciBudW3DqXJpY28gcXVlIGNvbnRpZW5lIGxvcyBwZXJpb2RvcyBwcmV2aW9zIGFsIHRyYXRhbWllbnRvIHNvYnJlIGxvcyBxdWUgZWwgcHJlZGljdG9yIHNlcsOhIHByb21lZGlhZG8uCiAgICAgICAgICAgICAgICAgICAgICAgICB0aW1lLm9wdGltaXplLnNzciA9IGMoMTk4MDoxOTg5KSwgICAgICAgICNVbiB2ZWN0b3IgbnVtw6lyaWNvIHF1ZSBpZGVudGlmaWNhIGxvcyBwZXJpb2RvcyBkZSBsYSB2YXJpYWJsZSBkZXBlbmRpZW50ZSBzb2JyZSBsb3MgY3VhbGVzIGxhIGZ1bmNpw7NuIGRlIHBlcmRpZGEgc2UgbWluaW1pemEgKGkuZS4sIGxhIG1pbmltaXphY2nDs24gZGUgbGEgc3VtYSBkZSBsb3MgcmVzaWR1b3MgYWwgY3VhZHJhZG8gZW50cmUgbGEgdW5pZGFkIGRlIHRyYXRhbWllbnRvIHkgbGEgdW5pZGFkIGRlIGNvbnRyb2wuKQogICAgICAgICAgICAgICAgICAgICAgICAgdW5pdC5uYW1lcy52YXJpYWJsZSA9ICJuYW1lIiwgICAgICAgICAgICAjTm9tYnJlIGRlIGxhcyB1bmlkYWRlcyBkZSBjb250cm9sCiAgICAgICAgICAgICAgICAgICAgICAgICB0aW1lLnBsb3QgPSAxOTgwOjE5OTYpICAgICAgICAgICAgICAgICAgICNVbiB2ZWN0b3IgcXVlIGlkZW50aWZpY2EgbG9zIHBlcsOtb2RvcyBkdXJhbnRlIGxvcyBjdWFsZXMgc2UgdHJhemFyw6FuIGxvcyByZXN1bHRhZG9zIGNvbiBnYXBzLnBsb3QgeSBwYXRoLnBsb3QuCgpgYGAKTm90ZSBxdWUgcGFyYSBnZW5lcmFyIGxhcyBtYXRyaWNlcyAkWF97MH0kIHRvbWFtb3MgbG9zIHByb21lZGlvcyBwb3IgYcOxbyBkZSBsYXMgb2JzZXJ2YWNpb25lcyBwYXJhIGNhZGEgdmFyaWFibGUgJFhfe2p9JCBjb24gJGo9MSwyLDMkIHkgbGFzIHZhcmlhYmxlcyBlc3BlY2lhbGVzIHNvbiBzaW1wbGVtZW50ZSBsYXMgb2JzZXJ2YWNpb25lcyBwYXJhICRZJCBkZSBsb3MgYcOxb3MgMTk4MCwgMTk4NSB5IDE5OTAuIAoKUGFyYSBnZW5lcmFyIGVsIGNvbnRyb2wgc2ludGV0aWNvLCBlcyBkZWNpciBlbCB2ZWN0b3IgZGUgcG9uZGVyYWRvcmVzICRXPShcb21lZ2FfezJ9LC4uLixcb21lZ2Ffe0orMX0pXntUfSQgdGFsIHF1ZSAKIAokJFlfezF0fV57Tn09XHN1bV97aj0yfV57SisxfSBcb21lZ2Ffe2p9IFlfe2p0fSQkCgoKZG9uZGUgJFlfezF0fV57Tn0kIGVzIGVsIG91dGNvbWUgZGUgaW50ZXLDqXMgZW4gYXVzZW5jaWEgZGUgbGEgaW50ZXZlbmNpw7NuLCBwYXJhICR0PlRfezB9JCwgZWwgcGVyaW9kbyBlbiBlbCBxdWUgb2N1cnJlIGxhIGludGVydmVuY2nDs24uCiAKCmBgYHtyfQpzeW50aC5vdXQgPC0gc3ludGgoZGF0YXByZXAub3V0KSAgICAgICAgICAgICNHZW5lcmEgZWwgdmVjdG9yIGRlIDZ4MSBlbnRyYWRhcyBkZWwgY29udHJvbCBzaW50ZXRpY28gcGFyYSBjYWRhIHByZWRpY3RvcgoKcm91bmQoc3ludGgub3V0JHNvbHV0aW9uLncsMikgICAgICAgICAgICAgICAjUmVkb25kZWEgeSBwcmVzZW50YSBsYXMgdmFyaWFibGVzIGEgZG9zIGNhcmFjdGVyZXMgZGVzcHXDqXMgZGVsIHB1bnRvIGRlY2ltYWwKCgpnYXBzPC0gZGF0YXByZXAub3V0JFkxcGxvdC0oCiAgICAgICAgZGF0YXByZXAub3V0JFkwcGxvdCUqJXN5bnRoLm91dCRzb2x1dGlvbi53KSAgI0VzdGEgZWN1YWNpw7NuIGVzIHNpbXBsZW1lbnRlIGVsIHRhdV8xdCBwYXJhIHRvZG9zIGxvcyBwZXJpb2Rvcy4KYGBgCgoKQ29tbyBoYWLDrWFtb3MgY29udmVuaWRvIGVuIGxhIGVudHJhZGEgZGUgdGVvcsOtYSBkZSBjb250cm9sIHNpbnTDqXRpY28gdW5vIGRlIGxvcyByZXF1ZXJpbWllbnRvcyBwYXJhIHZhbG9yYXIgZWwgdmVjdG9yICRXXnsqfSQgZXMgbGEgcmVnbGEgZGVsICpjb252ZXggaHVsbCosIGVzIGRlY2lyCgokJFhfezF9LVhfezB9V157Kn1cYXBwcm94IDAkJApgYGB7cn0KdChYMS4xKS0odChYMCklKiVzeW50aC5vdXQkc29sdXRpb24udykKYGBgCkRlIGFjdWVyZG8gY29uIGxhIHRlb3LDrWEsIHBhcmEgb2J0ZW5lciBtYXlvciBhcHJveGltYWNpw7NuIHRlbmRyaWFtb3MgcXVlIHV0aWxpemFyIG3DoXMgaW5mb3JtYWNpw7NuIHBhcmEgYW50ZXMgZGVsIHBlcmlvZG8gZGUgaW50ZXJ2ZW5jacOzbi4KCgpGaW5hbG1lbnRlLCBwYXJhIHByZXNlbnRhciBkZSBtYW5lcmEgZ3LDoWZpY2EgbG9zIGRhdG9zIHV0aWxpemFtb3MgbGFzIGZ1bmNpb25lcyBgcGF0aC5wbG90KClgIHBhcmEgZ3JhZmljYXIgbGFzIHRyYXllY3RvcmlhcyBvYnNlcnZhZGFzIHkgbGFzIHNpbnTDqXRpY2FzLCB5IGBnYXBzLnBsb3QoKWAgcGFyYSBncsOhZmljYXIgbGFzIGRpZmVyZW5jaWFzIGVudHJlIGxhIHNlcmllIG9ic2VydmFkYSB5IGxhIHNpbnTDqXRpY2EgZW4gdG9kbyBlbCBob3Jpem9udGUgdGVtb3BvcmFsLgoKYGBge3J9CnN5bnRoLnRhYmxlcyA8LSBzeW50aC50YWIoCiAgICAgIGRhdGFwcmVwLnJlcyA9IGRhdGFwcmVwLm91dCwKICAgICAgc3ludGgucmVzID0gc3ludGgub3V0KSAgICAgICAgICAgICAgICAgICAgICAgI1ByZXNlbnRhIGxvcyByZXN1bHRhZG9zIGRlbCBtb2RlbG8gZW4gdW5hIHRhYmxhCnByaW50KHN5bnRoLnRhYmxlcykKCgpwYXRoLnBsb3QoZGF0YXByZXAucmVzID0gZGF0YXByZXAub3V0LAogICAgICAgICAgc3ludGgucmVzID0gc3ludGgub3V0LAogICAgICAgICAgTWFpbiA9ICJUcmF5ZWN0b3JpYXMgZGUgY29udHJvbCBzaW50w6l0aWNvIikgICAgICAgICAgICAgICAgICAjR3JhZmljYSBsYXMgdHJheWVjdG9yaWFzIGRlIGxhIHVuaWRhZCB0cmF0YWRhIHkgc3UgdHJheWVjdG9yaWEgc2ludMOpdGljYQphYmxpbmUodiA9IDE5OTEsIGNvbCA9ICJyZWQiLCBsdHkgPSAyKQp0ZXh0KDE5OTEsIDEwMCwiUGVyaW9kbyBkZSBcbiBUcmF0YW1pZW50byAoVDApICIsCiAgICAgcG9zID0gMiwgY29sID0icmVkIikKCgpnYXBzLnBsb3QoZGF0YXByZXAucmVzID0gZGF0YXByZXAub3V0LAogICAgICAgICAgc3ludGgucmVzID0gc3ludGgub3V0LAogICAgICAgICAgTWFpbiA9ICJCcmVjaGFzIGRlbCBjb3Ryb2wgc2ludMOpdGljbyIpICAgICAgICAgICAgICAgICAgI0dyYWZpY2EgbGFzIGRpc2NyZXBhbmNpYXMgZW50cmUgbGEgdW5pZGFkIHRyYXRhZGEgeSBzdSB0cmF5ZWN0b3JpYSBzaW50w6l0aWNhCmFibGluZSh2ID0gMTk5MSwgY29sID0gInJlZCIsIGx0eSA9IDIpCnRleHQoMTk5MSwgMjAsICJQZXJpb2RvIGRlIFxuIFRyYXRhbWllbnRvIChUMCkgIiwKICAgICBwb3MgPSAyLCBjb2wgPSJyZWQiKQoKYGBgCgoKIyMjIFBhcXVldGVyw61hIGB0aWR5c3ludGhgCgpBaG9yYSB2YW1vcyBhIHRyYWJhamFyIGNvbiBsYSBsaWJyZXJpYSBgdGlkeXN5bnRoYCwgbG8gcGltZXJvIHF1ZSBoYXkgcXVlIGhhY2VyIGVzIGluc3RhbGFyIGxhIGxpYnJlcmlhOgoKCmBgYHtyfQpsaWJyYXJ5KHRpZHlzeW50aCkKYGBgCgoKTGEgYmFzZSBkZSBkYXRvcyBxdWUgdmFtb3MgYSB1dGlsaXphciBwcm92aWVuZSBkZSBBYmFkaWUgZXQgYWwuICgyMDEwKS4gTGEgYmFzZSBkZSBkYXRvcyBzZSBjb21wb25lIGRlIGxhIHNpZ3VpZW50ZSBtYW5lcmE6CgoKKiBMYSBjb2x1bW5hIGBzdGF0ZWAgZGUgY2xhc2UgKiJjaGFyYWN0ZXIqIiBjb250aWVuZSBsb3Mgbm9tYnJlcyBkZSBsb3MgZGlzdGludG9zIGVzdGFkb3MgZGUgRXN0YWRvcyBVbmlkb3MsCgoqIGxhIGNvbHVtbmEgYG51bWVyaWNgIGRlIGNsYXNlICIqbnVtZXJpYyoiIGNvbnRpZW5lIGVsIGHDsW8gZW4gcXVlIHNlIHJlYWxpemFuIGxhcyBvYnNlcnZhY2lvbmVzIGRlc2RlIGVsIGHDsW8gMTk3MCBhbCBhw7FvIDIwMDAsCgoqIGxhIGNvbHVtbmEgYGNpZ3NhbGVgIGRlIGNsYXNlICIqbnVtZXJpYyoiIGNvbnRpZW5lIGVsIHRvdGFsIGRlIGxhIHZlbnRhIGRlIGNpZ2FycmlsbG9zIHBlci1jYXBpdGEgcG9yIGVzdGFkbywKCiogbGEgY29sdW1uYSBgbG5pbmNvbWVgIGRlIGNsYXNlICIqbnVtZXJpYyoiIGNvbnRpZW5lIGVsIGxvZ2FyaXRtbyBkZWwgaW5ncmVzbyBwZXJzb25hbCBkZSBjYWRhIGVzdGFkbwoKKiBsYSBjb2x1bW5hIGBiZWVyYCBkZSBjbGFzZSAiKm51bWVyaWMqIiBjb250aWVuZSBlbCBjb25zdW1vIGRlIGNlcnZlemEgcGVyLWNhcGl0YQoKKiBsYSBjb2x1bW5hIGBhZ2UxNXRvMjRgIGRlIGNsYXNlICIqbnVtZXJpYyoiIGNvbnRpZW5lIGNvbnRpZW5lIGVsIHBvcmNlbnRhamUgZGUgbGEgcG9ibGFjacOzbiBlbnRyZSAxNSB5IDI0IGHDsW9zCgoqIGxhIGNvbHVtbmEgYHJldHByaWNlYCBkZSBjbGFzZSAiKm51bWVyaWMqIiBjb250aWVuZSBlbCBwcmVjaW8gcHJvbWVkaW8gZGUgKnJldGFpbCogZGUgbG9zIGNpZ2FycmlsbG9zLgoKRW4gZXN0ZSBlamVtcGxvLCBlbCAqb3V0Y29tZSogZGUgaW50ZXLDqXMgZXMgbGEgdmVudGEgZGUgY2lnYXJyaWxsb3MgcGVyLWNhcGl0YSwgeSBsYSB1bmlkYWQgZGUgdHJhdGFtaWVudG8gZXMgZWwgZXN0YWRvIGRlIENhbGlmb3JuaWEsIG1pZW50cmFzIHF1ZSBsYXMgdW5pZGFkZXMgZGUgY29ucnJvbCBzZXLDoW4gZWwgcmVzdG8gZGUgbG9zIGVzdGFvZHMgZGUgRXN0YWRvcyBVbmlkb3MuIExvIHF1ZSBzZSBpbnRlbnRhIGVzdGltYXIgZXMgbGEgdHJheWVjdG9yaWEgc2ludMOpdGljYSBkZSBsYSB2ZW50YSBkZSBjaWdhcnJpbGxvcyBlbiBDYWxpZm9ybmlhIGRlIG5vIGhhYmVyc2UgYXBsaWNhZG8gZWwgdHJhdGFtaWVudG8sIHF1ZSBlcywgbGEgbGxhbWFkYSAqUHJvcG9zaXRpb24gOTkqIHF1ZSBjb25zaXN0acOzIGVuIHVuIGF1bWVudG8gZGUgMjUgY2VudGF2b3MgcG9yIHBhcXVldGUgYSB0cmF2w6lzIGRlIHVuIGltcHVlc3RvLiAKCmBgYHtyfQpkYXRhKCJzbW9raW5nIikKYGBgCgpQYXJhIHByZXBhcmFyIGxvcyBkYXRvcyBjb21vIGVuIGxhIHBhcXVldGVyw61hIGBTeW50aGAgdXRpbGl6YW1vcyBsYSBmdW5jacOzbm0gYHN5bnRoZXRpY19jb250cm9sKClgIGRlIGxhIG1hbmVyYSBzaWd1aWVudGUsIGxhIG1hdHJpeiBwcmVkaWN0b3JlcyAkWF97MH0kIHkgJFhfezF9JCBjb24gbGEgZnVuY2nDs24gYGdlbmVyYXRlX3ByZWRpY3RvcnMoKWAsIHkgZ2VuZXJhciBsb3MgcG9uZGVyYWRvcmVzIGNvbiBsYSBmdW5jacOzbiBgZ2VuZXJhdGVfY29udHJvbHMoKWAuIERhZG8gcXVlIGxhIHBhcXVldGVyaWEgZXMgdW5hIGltcGxlbWVudGFjacOzbiBgdGlkeWAgbGEgY29uY2F0ZW5hY2nDs24gZGUgbGFzIGZ1bmNpb25lcyBzZSBoYWNlIGEgdHJhdsOpcyBkZWwgY29tYW5kbyBgJT4lYCwgY29tbyBzZSBtdWVzdHJhIGEgY29udGludWFjacOzbjoKCgpgYGB7cn0Kc21va2luZ19vdXQgPC0KICAKICBzbW9raW5nICU+JSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI0VzdGUgZXMgZWwgZGF0YSBmcmFtZSBxdWUgdmFtb3MgYSB1dGlsaXphcgogIHN5bnRoZXRpY19jb250cm9sKG91dGNvbWUgPSBjaWdzYWxlLCAgICAgICAgICAgICAjRWwgb3V0Y29tZSBkZSBpbnRlcsOpcwogICAgICAgICAgICAgICAgICAgIHVuaXQgPSBzdGF0ZSwgICAgICAgICAgICAgICAgICAjVmFtb3MgYSB1dGlsaXphciBsYSBjb2x1bW5hIHN0YXRlIGNvbW8gaW5kaWNlIGRlIGxhcyB1bmlkYWRlcwogICAgICAgICAgICAgICAgICAgIHRpbWUgPSB5ZWFyLCAgICAgICAgICAgICAgICAgICAjQ29uIGluZGljZSBkZSB0aWVtcG8gZGUgbGEgY29sdW1uYSB5ZWFyCiAgICAgICAgICAgICAgICAgICAgaV91bml0ID0gIkNhbGlmb3JuaWEiLCAgICAgICAgICNMYSB1bmlkYWQgZGUgdHJhdGFtaWVudG8gaj0xCiAgICAgICAgICAgICAgICAgICAgaV90aW1lID0gMTk4OCwgICAgICAgICAgICAgICAgICNFbCBwZXJpb2RvIGRlIHRyYXRhbWllbnRvIFQwCiAgICAgICAgICAgICAgICAgICAgZ2VuZXJhdGVfcGxhY2Vib3M9VCAgICAgICAgICAgICNHZW5lcmFyZW1vcyBwbGFjZWJvcyBwYXJhIGhhY2VyIGluZmVyZW5jaWEuCiAgICAgICAgICAgICAgICAgICAgKSAlPiUKICAKICAjIFZhbW9zIGEgZ2VuZXJhciBsb3MgcHJlZGljdG9yZXMgYWdyZWdhZG9zIHBhcmEgYWp1c3RhciBsb3MgcG9uZGVyYWRvcmVzOgogICMgcHJvbWVkaW8gZGVsIGxvZ2FyaXRtbyBkZWwgaW5ncmVzbywgZWwgcHJlY2lvIGRlIGxvcyBjaWdhcnJpbGxvcyB5IGxhIAogICMgcHJwb3JjaW9uIGRlIGxhIHBvYmxhY2nDs24gZW50cmUgMTUgeSAyNCBhw7FvcyBkZSAxOTgwIC0gMTk4OAogIAogIGdlbmVyYXRlX3ByZWRpY3Rvcih0aW1lX3dpbmRvdyA9IDE5ODA6MTk4OCwgICAgICAgICAgICAgICAgICMgTG9zIHBlcmlvZG9zIGhhc3RhIFQwPTE5ODgKICAgICAgICAgICAgICAgICAgICAgbG5faW5jb21lID0gbWVhbihsbmluY29tZSwgbmEucm0gPSBUKSwgICAjbG9nIGRlbCBpbmdyZXNvCiAgICAgICAgICAgICAgICAgICAgIHJldF9wcmljZSA9IG1lYW4ocmV0cHJpY2UsIG5hLnJtID0gVCksICAgI2VsIHByZWNpbyBkZSBsb3MgY2lnYXJyaWxsb3MKICAgICAgICAgICAgICAgICAgICAgeW91dGggPSBtZWFuKGFnZTE1dG8yNCwgbmEucm0gPSBUKSkgJT4lICAjcHJvcG9yY2lvbiBkZSBsYSBwb2IgZW50cmUgMTUgLSAyNCBhw7FvcwogIAogICMgcHJvbWVkaW8gZGVsIGNvbnN1bW8gZGUgY2VydmV6YSBlbiBlbCBkb25ub3IgcG9vbCBkZSAxOTg0IC0gMTk4OAogIGdlbmVyYXRlX3ByZWRpY3Rvcih0aW1lX3dpbmRvdyA9IDE5ODQ6MTk4OCwKICAgICAgICAgICAgICAgICAgICAgYmVlcl9zYWxlcyA9IG1lYW4oYmVlciwgbmEucm0gPSBUKSkgJT4lCiAgCiAgIyBsYXMgdmVudGFzIGRlIGNpZ2FycmlsbG9zIGF0cmFzYWRhcyAKICBnZW5lcmF0ZV9wcmVkaWN0b3IodGltZV93aW5kb3cgPSAxOTc1LAogICAgICAgICAgICAgICAgICAgICBjaWdzYWxlXzE5NzUgPSBjaWdzYWxlKSAlPiUKICBnZW5lcmF0ZV9wcmVkaWN0b3IodGltZV93aW5kb3cgPSAxOTgwLAogICAgICAgICAgICAgICAgICAgICBjaWdzYWxlXzE5ODAgPSBjaWdzYWxlKSAlPiUKICBnZW5lcmF0ZV9wcmVkaWN0b3IodGltZV93aW5kb3cgPSAxOTg4LAogICAgICAgICAgICAgICAgICAgICBjaWdzYWxlXzE5ODggPSBjaWdzYWxlKSAlPiUKICAKICAKICAjIGdlbmVyYSBsb3MgcG9uZGVyYWRvcmVzCiAgZ2VuZXJhdGVfd2VpZ2h0cyhvcHRpbWl6YXRpb25fd2luZG93ID0gMTk3MDoxOTg4LCAgIyBlbCBwZXJpb2RvIHBhcmEgb3B0aW1pemFjacOzbiBkZSBsb3MgcG9uZGVyYWRvcmVzCiAgICAgICAgICAgICAgICAgICBtYXJnaW5faXBvcCA9IC4wMiwgc2lnZl9pcG9wID0gNywgCiAgICAgICAgICAgICAgICAgICBib3VuZF9pcG9wID0gNiAgICAgICAgICAgICAgICAgICAgIyBvcGNpb25lcyBkZSBvcHRpbWl6YWNpw7NuCiAgKSAlPiUKICAKICAjIEdlbmVyYXIgZWwgY29udHJvbCBzaW50w6l0aWNvCiAgZ2VuZXJhdGVfY29udHJvbCgpCmBgYAoKUGFyYSBncmFmaWNhciBsYXMgdHJheWVjdG9yaWFzIGRlIGxhcyBzZXJpZXMgb2JzZXJ2YWRhcyB5IGxhIHNlcmllIHNpbnTDqXRpY2EgZGVsIHByb2JsZW1hIGFudGVyaW9yIHV0aWxpemFtb3MgZWwgc2lndWllbnRlIGNvbWFuZG86CgpgYGB7cn0Kc21va2luZ19vdXQgJT4lIHBsb3RfdHJlbmRzKCkgCmBgYApBZGVtw6FzLCBwb2RlbW9zIGdyYWZpY2FyIGxhcyBkaWZlcmVuY2lhcyBhYnNvbHV0YXMgZW50cmUgbGEgc2VyaWUgb2JzZXJ2YWRhIHkgbGEgc2VyaWUgc2ludMOpdGljYToKCmBgYHtyfQpzbW9raW5nX291dCAlPiUgcGxvdF9kaWZmZXJlbmNlcygpCmBgYAoKTG9zIHNpZ3VpZW50ZXMgaGlzdG9ncmFtYXMgbXVlc3RyYWwgbGEgcG9uZGVyYWNpw7NuIHF1ZSBlbCBwcm9ncmFtYSBsZSBkYSBhIGxhcyBkaWZlcmVudGVzIHVuaWRhZGVzLCBzaWVuZG8gVXRhaCBlbCBxdWUgcmVjaWJlIG1heW9yIHBvbmRlcmFjacOzbiB5IENvbmVjdGljdXQgZWwgcXVlIG1lbm9zIHBlcm8gZXN0cmljdGFtZW50ZSBwb3NpdGl2by4gQWRlbcOhcywgZGVsIGxhZG8gZGVyZWNobyBzZSBtdWVzdHJhbiBsYXMgcG9uZGVyYWNpb25lcyBwYXJhIGxhcyBkaWZlcm50ZXMgdmFyaWFibGVzIGVuIGxhIG1hdHJpeiBkZSBwcmVkaWN0b3JlcyBwYXJhIHRvZGFzIGxhcyB1bmlkYWRlcyAkW1hfezB9OlhfezF9XSQKClJlY29yZW1vcyBxdWUgdGFudG8gZXN0ZSBtw6l0b2RvIGNvbW8gZWwgZGUgbGEgcGFxdWV0ZXLDrWEgYFN5bnRoYCBnZW5lcmFuIGxvcyBwZXNvcyBiYWpvIGxhcyBzaWd1aWVudGVzIGNvbmRpY2lvbmVzOgoKJCRcc3VtX3tqPTJ9XntKKzF9XG9tZWdhX3tqfT0xJCQKIHkgCiQkXG9tZWdhX3tqfVxnZXEgMCBccXVhZCBcdGV4dHtjb259IFxxdWFkIGo9MiwuLi4sSisxJCQKYGBge3J9CnNtb2tpbmdfb3V0ICU+JSBwbG90X3dlaWdodHMoKQpgYGAKRGUgYWN1ZXJkbyBjb24gQWJhZGllIGV0LiBhbC4sICgyMDEwKSBlbCBtw6l0b2RvIGRlIGNvbnRyb2wgc2ludMOpdGljbyBwcm9kdWNlIHBvbmRlcmFkb3JlcyBpZ3VhbGVzIGEgY2VybyBwYXJhIGxhIG1heW9yw61hIGRlIGxhcyB1bmlkYWRlcyBkZWwgKmRvbm9yIHBvb2wqLgoKCgpgYGB7cn0Kc21va2luZ19vdXQgJT4lIGdyYWJfYmFsYW5jZV90YWJsZSgpCmBgYAojIyMjIEluZmVyZW5jaWEKCkVuIGN1YW50byBhIGxhIGluZmVyZW5jaWEsIGVsIG3DqXRvZG8gc2UgYmFzYSBlbiByZXBldGlyIGVsIG3DqXRvZG8gcGFyYSBjYWRhIGRvbmFudGUgZW4gZWwgZ3J1cG8gZGUgZG9uYW50ZXMgZXhhY3RhbWVudGUgZGUgbGEgbWlzbWEgbWFuZXJhIHF1ZSBzZSBoaXpvIHBhcmEgbGEgdW5pZGFkIHRyYXRhZGEsIGVzIGRlY2lyLCBnZW5lcmFuZG8gY29udHJvbGVzIHNpbnTDqXRpY29zIGRlIHBsYWNlYm8uIEFsIGVzdGFibGVjZXIgYGdlbmVyYXRlX3BsYWNlYm9zID0gVFJVRWAgYWwgaW5pY2lhbGl6YXIgbGEgdHViZXLDrWEgJ3N5bnRoJyBjb24gYHN5bnRoZXRpY19jb250cm9sKClgLCBsb3MgY2Fzb3MgZGUgcGxhY2VibyBzZSBnZW5lcmFuIGF1dG9tw6F0aWNhbWVudGUgYWwgY29uc3RydWlyIGVsIGNvbnRyb2wgc2ludMOpdGljbyBkZSBpbnRlcsOpcy4gRXN0byBmYWNpbGl0YSBsYSBleHBsb3JhY2nDs24gZGUgY3XDoW4gw7puaWNhIGVzIGxhIGRpZmVyZW5jaWEgZW50cmUgbGEgdW5pZGFkIG9ic2VydmFkYSB5IGxhIHVuaWRhZCBzaW50w6l0aWNhIGVuIGNvbXBhcmFjacOzbiBjb24gbG9zIHBsYWNlYm9zCgpgYGB7cn0Kc21va2luZ19vdXQgJT4lIAogIHBsb3RfcGxhY2Vib3MoKQpgYGAKCgpUZW4gZW4gY3VlbnRhIHF1ZSBsYSBmdW5jacOzbiBgcGxvdF9wbGFjZWJvcygpYCBhdXRvbcOhdGljYW1lbnRlIGVsaW1pbmEgY3VhbHF1aWVyIHBsYWNlYm8gcXVlIHNlIGFqdXN0ZSBtYWwgYSBsb3MgZGF0b3MgZW4gZWwgcGVyw61vZG8gcHJldmlvIGEgbGEgaW50ZXJ2ZW5jacOzbi4gTGEgcmF6w7NuIHBhcmEgaGFjZXJsbyBlcyBwdXJhbWVudGUgdmlzdWFsOiBlc2FzIHVuaWRhZGVzIHRpZW5kZW4gYSBkaXN0b3JzaW9uYXIgbGEgZXNjYWxhIGFsIHRyYXphciBsb3MgcGxhY2Vib3MuIFBhcmEgcmVhbGl6YXIgbGEgZWxpbWluYWNpw7NuLCBsYSBmdW5jacOzbiBleGFtaW5hIGVsIGVycm9yIGN1YWRyw6F0aWNvIG1lZGlvIGRlIHByZWRpY2Npw7NuIChNU1BFKSBlbiBlbCBwZXLDrW9kbyBwcmV2aW8gYSBsYSBpbnRlcnZlbmNpw7NuIChlcyBkZWNpciwgdW5hIG3DqXRyaWNhIHF1ZSByZWZsZWphIGN1w6FuIGJpZW4gc2UgYWp1c3RhIGVsIGNvbnRyb2wgc2ludMOpdGljbyBhIGxhIHNlcmllIGRlIHRpZW1wbyBkZSByZXN1bHRhZG9zIG9ic2VydmFkb3MgZW4gZWwgcGVyw61vZG8gcHJldmlvIGEgbGEgaW50ZXJ2ZW5jacOzbikuIFNpIHVuIGNvbnRyb2wgZGUgcGxhY2VibyB0aWVuZSB1biBNU1BFIHF1ZSBlcyBkb3MgdmVjZXMgbWF5b3IgcXVlIGVsIGNhc28gb2JqZXRpdm8gKHBvciBlamVtcGxvLCAiQ2FsaWZvcm5pYSIpLCBlbnRvbmNlcyBzZSBlbGltaW5hLiBQYXJhIGRlc2FjdGl2YXIgZXN0ZSBjb21wb3J0YW1pZW50bywgZXN0YWJsZWNlIGBwcnVuZSA9IEZBTFNFYC4KCmBgYHtyfQpzbW9raW5nX291dCAlPiUgcGxvdF9wbGFjZWJvcyhwcnVuZSA9IEZBTFNFKQpgYGAKCgpGaW5hbG1lbnRlLCBBZGFiaWUgZXQgYWwuIGVuIDIwMTAgZGVzY3JpYmVuIHVuYSBtYW5lcmEgZGUgY29uc3RydWlyIGxvcyB2YWxvcmVzIHAgZXhhY3RvcyBkZSBGaXNoZXIgZGl2aWRpZW5kbyBlbCBNU1BFIHBvc3RlcmlvciBhIGxhIGludGVydmVuY2nDs24gcG9yIGVsIE1TUEUgYW50ZXJpb3IgYSBsYSBpbnRlcnZlbmNpw7NuIHkgbHVlZ28gY2xhc2lmaWNhbmRvIHRvZG9zIGxvcyBjYXNvcyBlbiBvcmRlbiBkZXNjZW5kZW50ZSBzZWfDum4gZXN0YSBwcm9wb3JjacOzbi4gU2UgY29uc3RydXllIHVuIHZhbG9yIHAgdG9tYW5kbyBsYSBjbGFzaWZpY2FjacOzbiBlbnRyZSBlbCB0b3RhbCBkZSBjYXNvcy4gTGEgaWRlYSBzdWJ5YWNlbnRlIGVzIHF1ZSBzaSBlbCBjb250cm9sIHNpbnTDqXRpY28gc2UgYWp1c3RhIGJpZW4gYSBsYSBzZXJpZSBkZSB0aWVtcG8gb2JzZXJ2YWRhIChiYWpvIE1TUEUgZW4gZWwgcGVyw61vZG8gcHJldmlvKSB5IHNlIGRlc3bDrWEgZW4gZWwgcGVyw61vZG8gcG9zdGVyaW9yIChhbHRvIE1TUEUgZW4gZWwgcGVyw61vZG8gcG9zdGVyaW9yKSwgZW50b25jZXMgaGF5IHVuIGVmZWN0byBzaWduaWZpY2F0aXZvIGRlYmlkbyBhIGxhIGludGVydmVuY2nDs24uIFNpIGxhIGludGVydmVuY2nDs24gbm8gdHV2byBlZmVjdG8sIGVudG9uY2VzIGVsIHBlcsOtb2RvIHBvc3RlcmlvciB5IGVsIHBlcsOtb2RvIHByZXZpbyBkZWJlcsOtYW4gY29udGludWFyIG1hcGXDoW5kb3NlIGJhc3RhbnRlIGJpZW4sIGxvIHF1ZSBkYXLDrWEgY29tbyByZXN1bHRhZG8gdW5hIHByb3BvcmNpw7NuIGNlcmNhbmEgYSAxLiBTaSBsYXMgdW5pZGFkZXMgZGUgcGxhY2VibyBzZSBhanVzdGFuIGRlIG1hbmVyYSBzaW1pbGFyIGEgbG9zIGRhdG9zLCBlbnRvbmNlcyBubyBwb2RlbW9zIHJlY2hhemFyIGxhIGhpcMOzdGVzaXMgbnVsYSBkZSBxdWUgbGEgaW50ZXJ2ZW5jacOzbiBubyB0dXZvIGVmZWN0by4KCkVzdGEgcHJvcG9yY2nDs24gc2UgcHVlZGUgcmVwcmVzZW50YXIgZsOhY2lsbWVudGUgdXRpbGl6YW5kbyBgcGxvdF9tc3BlX3JhdGlvKClgLCBsbyBxdWUgb2ZyZWNlIGluZm9ybWFjacOzbiBzb2JyZSBsYSByYXJlemEgZGVsIGNhc28gZW4gZWwgcXVlIGxhIGludGVydmVuY2nDs24gcmVhbG1lbnRlIG9jdXJyacOzLgoKYGBge3J9CnNtb2tpbmdfb3V0ICU+JSBwbG90X21zcGVfcmF0aW8oKQpgYGAKCgpVbiBzZWd1bmRvIGVqZW1wbG86CgoKYGBge3J9CmxpYnJhcnkoU3ludGgpCmRhdGEoInN5bnRoLmRhdGEiKQoKc3ludGguZGF0YSA8LSBmaWx0ZXIoc3ludGguZGF0YSwgCiAgICAgICAgICAgICAgICAgICAgIHllYXIgPD0gMTk5NikKICAKc3ludGguZGF0YS5vdXQgPC0gCiAgc3ludGguZGF0YSAlPiUgCiAgc3ludGhldGljX2NvbnRyb2wob3V0Y29tZSA9IFksCiAgICAgICAgICAgICAgICAgICAgdW5pdCA9IG5hbWUsCiAgICAgICAgICAgICAgICAgICAgdGltZSA9IHllYXIsCiAgICAgICAgICAgICAgICAgICAgaV91bml0ID0gInRyZWF0ZWQucmVnaW9uIiwKICAgICAgICAgICAgICAgICAgICBpX3RpbWUgPSAxOTkxLAogICAgICAgICAgICAgICAgICAgIGdlbmVyYXRlX3BsYWNlYm9zID0gVAogICAgICAgICAgICAgICAgICAgICkgJT4lCiAgZ2VuZXJhdGVfcHJlZGljdG9yKHRpbWVfd2luZG93ID0gMTk4MDoxOTkxLAogICAgICAgICAgICAgICAgICAgeDEgPSBtZWFuKFgxLCBuYS5ybSA9IFQpLAogICAgICAgICAgICAgICAgICAgeDIgPSBtZWFuKFgyLCBuYS5ybSA9IFQpLAogICAgICAgICAgICAgICAgICAgeDMgPSBtZWFuKFgzLCBuYS5ybSA9IFQpCiAgICAgICAgICAgICAgICAgICApICAlPiUKICBnZW5lcmF0ZV9wcmVkaWN0b3IodGltZV93aW5kb3cgPSAxOTkxLAogICAgICAgICAgICAgICAgICAgICBZXzkxID0gWSkgJT4lCiAgZ2VuZXJhdGVfcHJlZGljdG9yKHRpbWVfd2luZG93ID0gMTk4NSwKICAgICAgICAgICAgICAgICAgICAgWV84NSA9IFkpICU+JQogIGdlbmVyYXRlX3ByZWRpY3Rvcih0aW1lX3dpbmRvdyA9IDE5ODAsCiAgICAgICAgICAgICAgICAgICAgIFlfODAgPSBZKSAlPiUKICBnZW5lcmF0ZV93ZWlnaHRzKG9wdGltaXphdGlvbl93aW5kb3cgPSAxOTcwOjE5OTEsCiAgICAgICAgICAgICAgICAgICBtYXJnaW5faXBvcCA9IC4wMiwgc2lnZl9pcG9wID0gNywgCiAgICAgICAgICAgICAgICAgICBib3VuZF9pcG9wID0gNgogICAgICAgICAgICAgICAgICAgKSAlPiUKICBnZW5lcmF0ZV9jb250cm9sKCkKCnN5bnRoLmRhdGEub3V0ICU+JQogIHBsb3RfdHJlbmRzKCkKYGBgCgoKYGBge3J9CnN5bnRoLmRhdGEub3V0ICU+JQogIHBsb3RfZGlmZmVyZW5jZXMoKQpgYGAKCgpgYGB7cn0Kc3ludGguZGF0YS5vdXQgJT4lCiAgcGxvdF93ZWlnaHRzKCkKYGBgCgoKCmBgYHtyfQpzeW50aC5kYXRhLm91dCAlPiUKICBncmFiX2JhbGFuY2VfdGFibGUoKQpgYGAKCjxicj48YnI+PGJyPjxicj48YnI+PGJyPgoKPHAgc3R5bGU9InRleHQtYWxpZ246Y2VudGVyOyI+CipDdWlkYXRlIGJiKgo8L3A+CgoK