Workflow for iterative building of a time series model.
We analyse the relative number of births per day in USA 1969-1988 using Gaussian process time series model with several model components that can explain the long term, seasonal, weekly, day of year, and special floatind day variation.
Models for relative number of birthdays
As the relative number of births is positive it’s natural to model the logarithm value. The generic form of the models is \[
y \sim \mbox{normal}(f(x), \sigma),
\] where \(f\) is different and gradually more complex function conditional on \(x\) that includes running day number, day of year, day of week and eventually some special floating US bank holidays.
Model 1: Slow trend
The model 1 is just the slow trend over the years using Hilbert space basis function approximated Gaussian process \[
f = \mbox{intercept} + f_1\\
\mbox{intercept} \sim \mbox{normal}(0,1)\\
f_1 \sim \mbox{GP}(0,K_1)
\] where GP has exponentiated quadratic covariance function.
In this phase the code from Riutort-Mayol et al.(2020) was cleaned and written to be more efficient, but only the one GP component was included to make the testing easier. Although the code was made more efficient, the aim wasn’t to make it the fastest possible as the later model changes may have bigger effect on the performance (it’s good o avoid premature optimization). We also use quite small number of basis functions to make the code run faster, and only later examine more carefully whether the number of basis function is sufficient compared to the posterior of the length scale (see, Riutort-Mayol et al., 2020).
Compile Stan model gpbf1.stan which includes gpbasisfun_functions1.stan
model1 <- cmdstan_model(stan_file = root("Birthdays", "gpbf1.stan"),
include_paths = root("Birthdays"))
Data to be passed to Stan
standata1 <- list(x=data$id,
y=log(data$births_relative100),
N=length(data$id),
c_f1=1.5, # factor c of basis functions for GP for f1
M_f1=20) # number of basis functions for GP for f1
As the basis function approximation and priors restrict the complexity of GP, we can safely use optimization to get a very quick initial result to check that the model code is computing what we intended. As there are only 14 parameters and 7305 observations it’s likely that the posterior is close to normal (in unconstrained space). In this case the optimization takes less than one second while MCMC sampling with default options would have taken several minutes. Although this result can be useful in a quick workflow, the result should not be used as the final result.
opt1 <- model1$optimize(data = standata1, init=0, algorithm='bfgs')
Check whether parameters have reasonable values
odraws1 <- opt1$draws()
subset(odraws1, variable=c('intercept','sigma_f1','lengthscale_f1','sigma'))
# A draws_matrix: 1 iterations, 1 chains, and 4 variables
variable
draw intercept sigma_f1 lengthscale_f1 sigma
1 -0.048 1.1 0.16 0.81
Compare the model to the data
oEf <- exp(as.numeric(subset(odraws1, variable='f')))
data %>%
mutate(oEf = oEf) %>%
ggplot(aes(x=date, y=births_relative100)) +
geom_point(color=set1[2], alpha=0.2) +
geom_line(aes(y=oEf), color=set1[1]) +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births")
After we get the model working using optimization we can compare the result to using short MCMC chains which will also provide us additional information on speed of different code implementations for the same model. We intentionally use just 1/10th length from the usual recommendation, as during the iterative model building a rough results are sufficient. When testing the code we initially used just one chain, but at this point running four chains with four core CPU doesn’t add much to the wall clock time, but gives more information of how easy it is sample from the posterior and can reveal if there are multiple modes. Although the result from short chains can be useful in a quick workflow, the result should not be used as th final result.
fit1 <- model1$sample(data=standata1, iter_warmup=100, iter_sampling=100,
chains=4, parallel_chains=4, seed=3891)
Depending on the random seed and luck, we sometimes observed that some of the chains got stuck in different modes. We could see this in high Rhat and low ESS diagnostic values.
draws1 <- fit1$draws()
summarise_draws(subset(draws1, variable=c('intercept','sigma_f1','lengthscale_f1','sigma')))
# A tibble: 4 × 10
variable mean median sd mad q5 q95 rhat ess_bulk ess_tail
<chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 intercept 0.083 0.096 0.15 0.083 -0.20 0.28 1.7 64. 108.
2 sigma_f1 0.39 0.29 0.22 0.18 0.17 0.75 2.2 5.6 24.
3 lengthscale_f1 1.7 1.7 1.5 2.2 0.19 3.2 1.8 6.3 30.
4 sigma 0.91 0.90 0.093 0.14 0.80 1.0 1.9 6.2 55.
Examining the trace plots shows the multimodality clearly.
mcmc_trace(draws1, regex_pars=c('intercept','sigma_f1','lengthscale_f1','sigma'))
In this case it was easy to figure out that some of the chains got stuck in qualitatively much worse modes. We don’t in general recommend to start from the mode as the mode is not usually representative point in hierarchical model posterior or in high dimensional posterior, but we can use this again to speed up the iterative model building as long as we check that the optimization result is sensible and later do more careful inference. Although the result from short chains can be useful in a quick workflow, the result should not be used as the final result.
init1 <- sapply(c('intercept','sigma_f1','lengthscale_f1','beta_f1','sigma'),
function(variable) {as.numeric(subset(odraws1, variable=variable))})
fit1 <- model1$sample(data=standata1, iter_warmup=100, iter_sampling=100,
chains=4, parallel_chains=4,
init=function() { init1 })
We now observe better Rhat and ESS diagnostic values, although due to very short chains they are not yet perfect. We are likely to also observe Hamiltonian Monte Carlo divergences and treedepth exceedences in dynamic building of the Hamiltonian trajectory, but there is no need to worry about those as long as the model results are qualitatively sensible as these computational issues can also go away when the model itself is improved. In all the following short MCMC samplings we get some or many divergences and usually very large number of treedepth exceedences. Divergences indicate possible bias and should be eventually investigated carefully. Treedepth exceedences indicate strong posterior dependencies and slow mixing and sometimes the posterior can be much improved by changing the parameterization or priors, but as the treedepth exceedences don’t indicate bias there is no need for more careful analysis if the resulting ESS and MCSE values are good for the purpose in hand. We’ll come back later to more careful analysis of the final models.
draws1 <- fit1$draws()
summarise_draws(subset(draws1, variable=c('intercept','sigma_f1','lengthscale_f1','sigma')))
# A tibble: 4 × 10
variable mean median sd mad q5 q95 rhat ess_bulk ess_tail
<chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 intercept 0.050 0.060 0.24 0.23 -0.37 0.38 1.0 296. 277.
2 sigma_f1 0.58 0.56 0.12 0.11 0.43 0.81 1.0 218. 336.
3 lengthscale_f1 0.23 0.23 0.039 0.037 0.17 0.29 1.0 224. 231.
4 sigma 0.81 0.81 0.0064 0.0068 0.80 0.82 1.0 400. 232.
Trace plot shows slow mixing but no multimodality.
mcmc_trace(draws1, regex_pars=c('intercept','sigma_f1','lengthscale_f1','sigma'))
The model result from short MCMC chains looks very similar to the optimization result.
draws1 <- as_draws_matrix(draws1)
Ef <- exp(apply(subset(draws1, variable='f'), 2, median))
data %>%
mutate(Ef = Ef) %>%
ggplot(aes(x=date, y=births_relative100)) + geom_point(color=set1[2], alpha=0.2) +
geom_line(aes(y=Ef), color=set1[1]) +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births")
If we compare the result from short sampling to optimizing, we don’t see practical difference in the predictions (although we see later more differences between optimization and MCMC).
data %>%
mutate(Ef = Ef,
oEf = oEf) %>%
ggplot(aes(x=Ef, y=oEf)) + geom_point(color=set1[2]) +
geom_abline() +
labs(x="Ef from short Markov chain", y="Ef from optimizing")
After the first version of this notebook, Nikolas Siccha examined more carefully the posterior correlations and noticed strong correlation between intercept and the first basis function. Stan’s dynamic HMC is so efficient that the inference is succesful anyway. Nikolas suggested removing the intercept term. The intercept term is not necessarily needed as the data has been centered. We test a model without the explicit intercept term.
Compile Stan model gpbf1b.stan
model1b <- cmdstan_model(stan_file = root("Birthdays", "gpbf1b.stan"),
include_paths = root("Birthdays"))
We sample using the default initialization.
fit1b <- model1b$sample(data=standata1, iter_warmup=100, iter_sampling=100,
chains=4, parallel_chains=4, seed=3891)
The sampling performs better, indicating that the strong posterior correlation in the first model was causing troubles for the adaptation in the short warmup leading some chains to stay stuck.
draws1b <- fit1b$draws()
summarise_draws(subset(draws1b, variable=c('sigma_f1','lengthscale_f1','sigma')))
# A tibble: 3 × 10
variable mean median sd mad q5 q95 rhat ess_bulk ess_tail
<chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 sigma_f1 0.60 0.58 0.14 0.12 0.41 0.87 1.0 221. 215.
2 lengthscale_f1 0.23 0.23 0.042 0.036 0.15 0.29 1.0 198. 252.
3 sigma 0.81 0.81 0.0068 0.0067 0.80 0.82 1.0 401. 313.
Examining the trace plots don’t show multimodality
mcmc_trace(draws1b, regex_pars=c('sigma_f1','lengthscale_f1','sigma'))
We drop global intercept from the rest of the models, but continue using (early stopped) optimization to initialize the sampling.
Model 2: Slow trend + yearly seasonal trend
The model 2 adds yearly seasonal trend using GP with periodic covariance function. \[
f = \mbox{intercept} + f_1 + f_2 \\
\mbox{intercept} \sim \mbox{normal}(0,1)\\
f_1 \sim \mbox{GP}(0,K_1)\\
f_2 \sim \mbox{GP}(0,K_2)
\] where the first GP uses the exponentiated quadratic covariance function, and the second one a periodic covariance function. Most years have 365 calendar days and every four years (during the data range) there are 366 days, and thus we simplify and use period of 365.25 for the periodic component,
The first version of model 2 with the added periodic component following from Riutort-Mayol (2020) turned out be very slow. With the default MCMC options the inference would have taken hours, but with the short chains it was possible to infer that something has to be wrong. The model output was sensible, but diagnostics indicated very slow mixing. By more careful examination of the model it turned out that the periodic component was including another intercept term and with two intercept terms their sum was well informed by the data, but individually they were not well informed and thus the posteriors were wide, which lead to very slow mixing. This bad model is not shown here, but the optimization, short MCMC chains and sampling diagnostic tools were crucial for fast experimentation and solving the problem.
Compile Stan model 2 (the fixed version) gpbf2.stan
model2 <- cmdstan_model(stan_file = root("Birthdays", "gpbf2.stan"),
include_paths = root("Birthdays"))
Data to be passed to Stan
standata2 <- list(x=data$id,
y=log(data$births_relative100),
N=length(data$id),
c_f1=1.5, # factor c of basis functions for GP for f1
M_f1=20, # number of basis functions for GP for f1
J_f2=20) # number of basis functions for periodic f2
Optimizing is faster than sampling (although this result can be useful in a quick workflow, the result should not be used as the final result).
opt2 <- model2$optimize(data=standata2, init=0, algorithm='bfgs')
Check whether parameters have reasonable values
odraws2 <- opt2$draws()
subset(odraws2, variable=c('sigma_','lengthscale_','sigma'), regex=TRUE)
# A draws_matrix: 1 iterations, 1 chains, and 5 variables
variable
draw sigma_f1 sigma_f2 lengthscale_f1 lengthscale_f2 sigma
1 1.4 1.3 0.16 0.087 0.75
Compare the model to the data
Ef <- exp(as.numeric(subset(odraws2, variable='f')))
Ef1 <- as.numeric(subset(odraws2, variable='f1'))
Ef1 <- exp(Ef1 - mean(Ef1) + mean(log(data$births_relative100)))
Ef2 <- as.numeric(subset(odraws2, variable='f2'))
Ef2 <- exp(Ef2 - mean(Ef2) + mean(log(data$births_relative100)))
pf <- data %>%
mutate(Ef = Ef) %>%
ggplot(aes(x=date, y=births_relative100)) + geom_point(color=set1[2], alpha=0.2) +
geom_line(aes(y=Ef), color=set1[1]) +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births")
pf1 <- data %>%
mutate(Ef1 = Ef1) %>%
ggplot(aes(x=date, y=births_relative100)) + geom_point(color=set1[2], alpha=0.2) +
geom_line(aes(y=Ef1), color=set1[1]) +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births")
pf2 <- data %>%
mutate(Ef2 = Ef2) %>%
group_by(day_of_year2) %>%
summarise(meanbirths=mean(births_relative100), meanEf2=mean(Ef2)) %>%
ggplot(aes(x=as.Date("1987-12-31")+day_of_year2, y=meanbirths)) + geom_point(color=set1[2], alpha=0.2) +
scale_x_date(date_breaks = "1 month", date_labels = "%b") +
geom_line(aes(y=meanEf2), color=set1[1]) +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births of year")
pf / (pf1 + pf2)
Sample short chains using the optimization result as initial values (although the result from short chains can be useful in a quick workflow, the result should not be used as the final result).
init2 <- sapply(c('lengthscale_f1','lengthscale_f2','sigma_f1','sigma_f2','sigma','beta_f1','beta_f2'),
function(variable) {as.numeric(subset(odraws2, variable=variable))})
fit2 <- model2$sample(data=standata2, iter_warmup=100, iter_sampling=100,
chains=4, parallel_chains=4,
init=function() { init2 })
Check whether parameters have reasonable values
draws2 <- fit2$draws()
summarise_draws(subset(draws2, variable=c('sigma_','lengthscale_','sigma'), regex=TRUE))
# A tibble: 5 × 10
variable mean median sd mad q5 q95 rhat ess_bulk ess_tail
<chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 sigma_f1 0.57 0.56 0.12 0.12 0.39 0.82 1.0 231. 382.
2 sigma_f2 0.29 0.29 0.053 0.044 0.23 0.38 1.0 276. 408.
3 lengthscale_f1 0.21 0.21 0.039 0.036 0.14 0.26 1.0 333. 361.
4 lengthscale_f2 0.24 0.24 0.026 0.028 0.20 0.29 1.0 274. 300.
5 sigma 0.75 0.75 0.0059 0.0055 0.74 0.76 1.0 398. 260.
Compare the model to the data
draws2 <- as_draws_matrix(draws2)
Ef <- exp(apply(subset(draws2, variable='f'), 2, median))
Ef1 <- apply(subset(draws2, variable='f1'), 2, median)
Ef1 <- exp(Ef1 - mean(Ef1) + mean(log(data$births_relative100)))
Ef2 <- apply(subset(draws2, variable='f2'), 2, median)
Ef2 <- exp(Ef2 - mean(Ef2) + mean(log(data$births_relative100)))
pf <- data %>%
mutate(Ef = Ef) %>%
ggplot(aes(x=date, y=births_relative100)) + geom_point(color=set1[2], alpha=0.2) +
geom_line(aes(y=Ef), color=set1[1]) +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births")
pf1 <- data %>%
mutate(Ef1 = Ef1) %>%
ggplot(aes(x=date, y=births_relative100)) + geom_point(color=set1[2], alpha=0.2) +
geom_line(aes(y=Ef1), color=set1[1]) +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births")
pf2 <- data %>%
mutate(Ef2 = Ef2) %>%
group_by(day_of_year2) %>%
summarise(meanbirths=mean(births_relative100), meanEf2=mean(Ef2)) %>%
ggplot(aes(x=as.Date("1987-12-31")+day_of_year2, y=meanbirths)) + geom_point(color=set1[2], alpha=0.2) +
scale_x_date(date_breaks = "1 month", date_labels = "%b") +
geom_line(aes(y=meanEf2), color=set1[1]) +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births of year")
pf / (pf1 + pf2)
Seasonal component has reasonable fit to the data.
Model 3: Slow trend + yearly seasonal trend + day of week
Based on the quick plotting of the data above, day of week has a clear effect and there are less babies born on Saturday and Sunday. This can be taken into account with simple additive coefficients. We fix the effect of Monday to 0 and have additional coefficients for other weekdays. \[
f = \mbox{intercept} + f_1 + f_2 + \beta_{\mbox{day of week}} \\
\mbox{intercept} \sim \mbox{normal}(0,1)\\
f_1 \sim \mbox{GP}(0,K_1)\\
f_2 \sim \mbox{GP}(0,K_2)\\
\beta_{\mbox{day of week}} = 0 \quad \mbox{if day of week is Monday}\\
\beta_{\mbox{day of week}} \sim \mbox{normal}(0,1) \quad \mbox{if day of week is not Monday}
\]
Compile Stan model 3 gpbf3.stan
model3 <- cmdstan_model(stan_file = root("Birthdays", "gpbf3.stan"),
include_paths = root("Birthdays"))
Data to be passed to Stan
standata3 <- list(x=data$id,
y=log(data$births_relative100),
N=length(data$id),
c_f1=1.5, # factor c of basis functions for GP for f1
M_f1=20, # number of basis functions for GP for f1
J_f2=20, # number of basis functions for periodic f2
day_of_week=data$day_of_week)
Optimizing is faster than sampling (although this result can be useful in a quick workflow, the result should not be used as the final result).
opt3 <- model3$optimize(data=standata3, init=0, algorithm='bfgs')
Check whether parameters have reasonable values
odraws3 <- opt3$draws()
subset(odraws3, variable=c('sigma_','lengthscale_','sigma'), regex=TRUE)
# A draws_matrix: 1 iterations, 1 chains, and 5 variables
variable
draw sigma_f1 sigma_f2 lengthscale_f1 lengthscale_f2 sigma
1 1.6 1.3 0.16 0.087 0.33
subset(odraws3, variable=c('beta_f3'))
# A draws_matrix: 1 iterations, 1 chains, and 6 variables
variable
draw beta_f3[1] beta_f3[2] beta_f3[3] beta_f3[4] beta_f3[5] beta_f3[6]
1 0.36 0.12 0.04 0.17 -1.1 -1.5
Compare the model to the data
Ef <- exp(as.numeric(subset(odraws3, variable='f')))
Ef1 <- as.numeric(subset(odraws3, variable='f1'))
Ef1 <- exp(Ef1 - mean(Ef1) + mean(log(data$births_relative100)))
Ef2 <- as.numeric(subset(odraws3, variable='f2'))
Ef2 <- exp(Ef2 - mean(Ef2) + mean(log(data$births_relative100)))
Ef_day_of_week <- as.numeric(subset(odraws3, variable='f_day_of_week'))
Ef_day_of_week <- exp(Ef_day_of_week - mean(Ef_day_of_week) + mean(log(data$births_relative100)))
pf <- data %>%
mutate(Ef = Ef) %>%
ggplot(aes(x=date, y=births_relative100)) + geom_point(color=set1[2], alpha=0.2) +
geom_line(aes(y=Ef), color=set1[1], alpha=0.75) +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births")
pf1 <- data %>%
mutate(Ef1 = Ef1) %>%
ggplot(aes(x=date, y=births_relative100)) + geom_point(color=set1[2], alpha=0.2) +
geom_line(aes(y=Ef1), color=set1[1]) +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births")
pf2 <- data %>%
mutate(Ef2 = Ef2) %>%
group_by(day_of_year2) %>%
summarise(meanbirths=mean(births_relative100), meanEf2=mean(Ef2)) %>%
ggplot(aes(x=as.Date("1987-12-31")+day_of_year2, y=meanbirths)) + geom_point(color=set1[2], alpha=0.2) +
scale_x_date(date_breaks = "1 month", date_labels = "%b") +
geom_line(aes(y=meanEf2), color=set1[1]) +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births of year")
pf3 <- ggplot(data=data, aes(x=day_of_week, y=births_relative100)) + geom_point(color=set1[2], alpha=0.2) +
scale_x_continuous(breaks = 1:7, labels=c('Mon','Tue','Wed','Thu','Fri','Sat','Sun')) +
geom_line(data=data.frame(x=1:7,y=Ef_day_of_week), aes(x=x, y=Ef_day_of_week), color=set1[1]) +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births of week")
(pf + pf1) / (pf2 + pf3)
Sample short chains using the optimization result as initial values (although the result from short chains can be useful in a quick workflow, the result should not be used as the final result).
init3 <- sapply(c('lengthscale_f1','lengthscale_f2','sigma_f1','sigma_f2','sigma',
'beta_f1','beta_f2','beta_f3'),
function(variable) {as.numeric(subset(odraws3, variable=variable))})
fit3 <- model3$sample(data=standata3, iter_warmup=100, iter_sampling=100,
chains=4, parallel_chains=4,
init=function() { init3 })
Check whether parameters have reasonable values
draws3 <- fit3$draws()
summarise_draws(subset(draws3, variable=c('sigma_','lengthscale_','sigma'), regex=TRUE))
# A tibble: 5 × 10
variable mean median sd mad q5 q95 rhat ess_bulk ess_tail
<chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 sigma_f1 0.62 0.59 0.14 0.13 0.44 0.88 1.0 306. 333.
2 sigma_f2 0.28 0.28 0.047 0.040 0.22 0.36 1.0 137. 341.
3 lengthscale_f1 0.21 0.21 0.038 0.034 0.14 0.26 1.0 340. 303.
4 lengthscale_f2 0.21 0.21 0.019 0.019 0.18 0.24 1.0 148. 226.
5 sigma 0.33 0.33 0.0027 0.0026 0.33 0.33 1.0 475. 320.
summarise_draws(subset(draws3, variable=c('beta_f3')))
# A tibble: 6 × 10
variable mean median sd mad q5 q95 rhat ess_bulk ess_tail
<chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 beta_f3[1] 0.36 0.36 0.014 0.014 0.33 0.38 1.0 381. 246.
2 beta_f3[2] 0.13 0.13 0.015 0.016 0.10 0.15 1.0 434. 339.
3 beta_f3[3] 0.041 0.042 0.014 0.014 0.018 0.065 1.0 380. 285.
4 beta_f3[4] 0.17 0.17 0.015 0.015 0.15 0.20 1.0 369. 334.
5 beta_f3[5] -1.1 -1.1 0.015 0.014 -1.1 -1.1 1.0 387. 340.
6 beta_f3[6] -1.5 -1.5 0.014 0.015 -1.5 -1.5 1.0 433. 339.
Compare the model to the data
draws3 <- as_draws_matrix(draws3)
Ef <- exp(apply(subset(draws3, variable='f'), 2, median))
Ef1 <- apply(subset(draws3, variable='f1'), 2, median)
Ef1 <- exp(Ef1 - mean(Ef1) + mean(log(data$births_relative100)))
Ef2 <- apply(subset(draws3, variable='f2'), 2, median)
Ef2 <- exp(Ef2 - mean(Ef2) + mean(log(data$births_relative100)))
Ef_day_of_week <- apply(subset(draws3, variable='f_day_of_week'), 2, median)
Ef_day_of_week <- exp(Ef_day_of_week - mean(Ef_day_of_week) + mean(log(data$births_relative100)))
pf <- data %>%
mutate(Ef = Ef) %>%
ggplot(aes(x=date, y=births_relative100)) + geom_point(color=set1[2], alpha=0.2) +
geom_line(aes(y=Ef), color=set1[1], alpha=0.75) +
labs(x="Date", y="Relative number of births")
pf1 <- data %>%
mutate(Ef1 = Ef1) %>%
ggplot(aes(x=date, y=births_relative100)) + geom_point(color=set1[2], alpha=0.2) +
geom_line(aes(y=Ef1), color=set1[1]) +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births")
pf2 <- data %>%
mutate(Ef2 = Ef2) %>%
group_by(day_of_year2) %>%
summarise(meanbirths=mean(births_relative100), meanEf2=mean(Ef2)) %>%
ggplot(aes(x=as.Date("1987-12-31")+day_of_year2, y=meanbirths)) + geom_point(color=set1[2], alpha=0.2) +
scale_x_date(date_breaks = "1 month", date_labels = "%b") +
geom_line(aes(y=meanEf2), color=set1[1]) +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births of year")
pf3 <- ggplot(data=data, aes(x=day_of_week, y=births_relative100)) + geom_point(color=set1[2], alpha=0.2) +
scale_x_continuous(breaks = 1:7, labels=c('Mon','Tue','Wed','Thu','Fri','Sat','Sun')) +
geom_line(data=data.frame(x=1:7,y=Ef_day_of_week), aes(x=x, y=Ef_day_of_week), color=set1[1]) +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births of week")
(pf + pf1) / (pf2 + pf3)
Weekday effects are easy to estimate as there are about thousand observations per weekday.
Model 4: long term smooth + seasonal + weekday with increasing magnitude
Looking at the time series of whole data we see the dots representing the daily values forming three branches that are getting further away from each other. In previous analysis (BDA3) we also had a model component allowing gradually changing effect for day of week and did observe that the effect of Saturday and Sunday did get stronger in time. The next model includes time dependent magnitude component for the day of week effect. \[
f = \mbox{intercept} + f_1 + f_2 + \exp(g_3)\beta_{\mbox{day of week}} \\
\mbox{intercept} \sim \mbox{normal}(0,1)\\
f_1 \sim \mbox{GP}(0,K_1)\\
f_2 \sim \mbox{GP}(0,K_2)\\
g_3 \sim \mbox{GP}(0,K_3)\\
\beta_{\mbox{day of week}} = 0 \quad \mbox{if day of week is Monday}\\
\beta_{\mbox{day of week}} \sim \mbox{normal}(0,1) \quad \mbox{if day of week is not Monday}
\] The magnitude of the weekday effect is modelled with \(\exp(g_3)\), where \(g_3\) has GP prior with zero mean and exponentiated quadratic covariance function.
Compile Stan model 4 gpbf4.stan
model4 <- cmdstan_model(stan_file = root("Birthdays", "gpbf4.stan"),
include_paths = root("Birthdays"))
Data to be passed to Stan
standata4 <- list(x=data$id,
y=log(data$births_relative100),
N=length(data$id),
c_f1=1.5, # factor c of basis functions for GP for f1
M_f1=20, # number of basis functions for GP for f1
J_f2=20, # number of basis functions for periodic f2
c_g3=1.5, # factor c of basis functions for GP for g3
M_g3=5, # number of basis functions for GP for g3
day_of_week=data$day_of_week)
As we have increased the complexity of the model, the mode starts to be less and less representative of the posterior. We still use the optimization to check that code returns something reasonable and as initial values for MCMC, but we now stop the optimization early. By adding tol_obj=10
argument, the optimization stops when the change in the log posterior density is less than 10, which is likely to happened before reaching the mode.
opt4 <- model4$optimize(data=standata4, init=0, algorithm='bfgs', tol_obj=10)
Check whether parameters have reasonable values
odraws4 <- opt4$draws()
subset(odraws4, variable=c('sigma_','lengthscale_','sigma'), regex=TRUE)
# A draws_matrix: 1 iterations, 1 chains, and 7 variables
variable
draw sigma_f1 sigma_f2 sigma_g3 lengthscale_f1 lengthscale_f2 lengthscale_g3 sigma
1 0.7 0.69 0.4 0.15 0.24 0.75 0.31
subset(odraws4, variable=c('beta_f3'))
# A draws_matrix: 1 iterations, 1 chains, and 6 variables
variable
draw beta_f3[1] beta_f3[2] beta_f3[3] beta_f3[4] beta_f3[5] beta_f3[6]
1 0.39 0.14 0.06 0.2 -1.3 -1.7
Compare the model to the data
Ef <- exp(as.numeric(subset(odraws4, variable='f')))
Ef1 <- as.numeric(subset(odraws4, variable='f1'))
Ef1 <- exp(Ef1 - mean(Ef1) + mean(log(data$births_relative100)))
Ef2 <- as.numeric(subset(odraws4, variable='f2'))
Ef2 <- exp(Ef2 - mean(Ef2) + mean(log(data$births_relative100)))
Ef_day_of_week <- as.numeric(subset(odraws4, variable='f_day_of_week'))
Ef_day_of_week <- exp(Ef_day_of_week - mean(Ef_day_of_week) + mean(log(data$births_relative100)))
Ef3 <- as.numeric(subset(odraws4, variable='f3'))
Ef3 <- exp(Ef3 - mean(Ef3) + mean(log(data$births_relative100)))
pf <- data %>%
mutate(Ef = Ef) %>%
ggplot(aes(x=date, y=births_relative100)) + geom_point(color=set1[2], alpha=0.2) +
geom_line(aes(y=Ef), color=set1[1], alpha=0.75) +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births")
pf1 <- data %>%
mutate(Ef1 = Ef1) %>%
ggplot(aes(x=date, y=births_relative100)) + geom_point(color=set1[2], alpha=0.2) +
geom_line(aes(y=Ef1), color=set1[1]) +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births")
pf2 <- data %>%
mutate(Ef2 = Ef2) %>%
group_by(day_of_year2) %>%
summarise(meanbirths=mean(births_relative100), meanEf2=mean(Ef2)) %>%
ggplot(aes(x=as.Date("1987-12-31")+day_of_year2, y=meanbirths)) + geom_point(color=set1[2], alpha=0.2) +
scale_x_date(date_breaks = "1 month", date_labels = "%b") +
geom_line(aes(y=meanEf2), color=set1[1]) +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births of year")
pf3 <- ggplot(data=data, aes(x=day_of_week, y=births_relative100)) + geom_point(color=set1[2], alpha=0.2) +
scale_x_continuous(breaks = 1:7, labels=c('Mon','Tue','Wed','Thu','Fri','Sat','Sun')) +
geom_line(data=data.frame(x=1:7,y=Ef_day_of_week), aes(x=x, y=Ef_day_of_week), color=set1[1]) +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births of week")
pf3b <- data %>%
mutate(Ef3 = Ef3) %>%
ggplot(aes(x=date, y=births_relative100/Ef1/Ef2*100*100)) + geom_point(color=set1[2], alpha=0.2) +
geom_point(aes(y=Ef3), color=set1[1], size=0.1) +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births")
(pf + pf1) / (pf2 + pf3b)
Sample short chains using the early stopped optimization result as initial values (although the result from short chains can be useful in a quick workflow, the result should not be used as the final result).
init4 <- sapply(c('lengthscale_f1','lengthscale_f2','lengthscale_g3',
'sigma_f1','sigma_f2','sigma_g3','sigma',
'beta_f1','beta_f2','beta_f3','beta_g3'),
function(variable) {as.numeric(subset(odraws4, variable=variable))})
fit4 <- model4$sample(data=standata4, iter_warmup=100, iter_sampling=100,
chains=4, parallel_chains=4,
init=function() { init4 }, refresh=10)
Check whether parameters have reasonable values
draws4 <- fit4$draws()
summarise_draws(subset(draws4, variable=c('sigma_','lengthscale_','sigma'), regex=TRUE))
# A tibble: 7 × 10
variable mean median sd mad q5 q95 rhat ess_bulk ess_tail
<chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 sigma_f1 0.64 0.62 0.13 0.11 0.47 0.87 1.0 237. 281.
2 sigma_f2 0.29 0.28 0.043 0.038 0.23 0.37 1.0 256. 338.
3 sigma_g3 0.19 0.18 0.047 0.046 0.13 0.28 1.0 614. 282.
4 lengthscale_f1 0.21 0.21 0.034 0.032 0.14 0.25 1.0 356. 316.
5 lengthscale_f2 0.20 0.20 0.018 0.016 0.18 0.24 1.1 56. 230.
6 lengthscale_g3 0.76 0.76 0.19 0.19 0.44 1.0 1.0 553. 383.
7 sigma 0.31 0.31 0.0026 0.0026 0.31 0.31 1.0 270. 252.
summarise_draws(subset(draws4, variable=c('beta_f3')))
# A tibble: 6 × 10
variable mean median sd mad q5 q95 rhat ess_bulk ess_tail
<chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 beta_f3[1] 0.36 0.36 0.042 0.040 0.30 0.43 1.0 245. 334.
2 beta_f3[2] 0.13 0.13 0.021 0.020 0.10 0.17 1.0 317. 313.
3 beta_f3[3] 0.052 0.052 0.015 0.015 0.029 0.075 1.0 402. 368.
4 beta_f3[4] 0.18 0.18 0.024 0.022 0.14 0.22 1.0 290. 303.
5 beta_f3[5] -1.2 -1.1 0.13 0.12 -1.4 -0.96 1.0 218. 326.
6 beta_f3[6] -1.6 -1.5 0.17 0.16 -1.8 -1.3 1.0 220. 318.
Compare the model to the data
draws4 <- as_draws_matrix(draws4)
Ef <- exp(apply(subset(draws4, variable='f'), 2, median))
Ef1 <- apply(subset(draws4, variable='f1'), 2, median)
Ef1 <- exp(Ef1 - mean(Ef1) + mean(log(data$births_relative100)))
Ef2 <- apply(subset(draws4, variable='f2'), 2, median)
Ef2 <- exp(Ef2 - mean(Ef2) + mean(log(data$births_relative100)))
Ef_day_of_week <- apply(subset(draws4, variable='f_day_of_week'), 2, median)
Ef_day_of_week <- exp(Ef_day_of_week - mean(Ef_day_of_week) + mean(log(data$births_relative100)))
Ef3 <- apply(subset(draws4, variable='f3'), 2, median)
Ef3 <- exp(Ef3 - mean(Ef3) + mean(log(data$births_relative100)))
pf <- data %>%
mutate(Ef = Ef) %>%
ggplot(aes(x=date, y=births_relative100)) + geom_point(color=set1[2], alpha=0.2) +
geom_line(aes(y=Ef), color=set1[1], alpha=0.75) +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births")
pf1 <- data %>%
mutate(Ef1 = Ef1) %>%
ggplot(aes(x=date, y=births_relative100)) + geom_point(color=set1[2], alpha=0.2) +
geom_line(aes(y=Ef1), color=set1[1]) +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births")
pf2 <- data %>%
mutate(Ef2 = Ef2) %>%
group_by(day_of_year2) %>%
summarise(meanbirths=mean(births_relative100), meanEf2=mean(Ef2)) %>%
ggplot(aes(x=as.Date("1987-12-31")+day_of_year2, y=meanbirths)) + geom_point(color=set1[2], alpha=0.2) +
scale_x_date(date_breaks = "1 month", date_labels = "%b") +
geom_line(aes(y=meanEf2), color=set1[1]) +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births of year")
pf3 <- ggplot(data=data, aes(x=day_of_week, y=births_relative100)) + geom_point(color=set1[2], alpha=0.2) +
scale_x_continuous(breaks = 1:7, labels=c('Mon','Tue','Wed','Thu','Fri','Sat','Sun')) +
geom_line(data=data.frame(x=1:7,y=Ef_day_of_week), aes(x=x, y=Ef_day_of_week), color=set1[1]) +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births of week")
pf3b <- data %>%
mutate(Ef3 = Ef3) %>%
ggplot(aes(x=date, y=births_relative100/Ef1/Ef2*100*100)) + geom_point(color=set1[2], alpha=0.2) +
geom_point(aes(y=Ef3), color=set1[1], size=0.1) +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births")
(pf + pf1) / (pf2 + pf3b)
The model fits well the different branches visible in plotted daily relative number of births, that is, it is able to model the increasing weekend effect.
Model 5: long term smooth + seasonal + weekday with time dependent magnitude + day of year RHS
The next component to add is day of year effect. Many bank holidays are every year on the same day of year and there might be also other special days that are favored or disfavored.
\[
f = \mbox{intercept} + f_1 + f_2 + \exp(g_3)\beta_{\mbox{day of week}} + \beta_{\mbox{day of year}}\\
\mbox{intercept} \sim \mbox{normal}(0,1)\\
f_1 \sim \mbox{GP}(0,K_1)\\
f_2 \sim \mbox{GP}(0,K_2)\\
g_3 \sim \mbox{GP}(0,K_3)\\
\beta_{\mbox{day of week}} = 0 \quad \mbox{if day of week is Monday}\\
\beta_{\mbox{day of week}} \sim \mbox{normal}(0,1) \quad \mbox{if day of week is not Monday}\\
\beta_{\mbox{day of year}} \sim RHS(0,0.1)
\] As we assume that only some days of year are special, we use regularized horseshoe (RHS) prior for day of year effects.
At this point the optimization didn’t produce reasonable result as earlier and sampling turned out to be very slow. We assumed the optimization fails because there were so many more parameters with hierarchical prior. As even the short chain sampling would have taken more than hour, it would have been time consuming to further to test the model. As part of the quick iterative model building it was better to give up on this model for a moment.
Compile Stan model 5 gpbf5.stan
model5 <- cmdstan_model(stan_file = root("Birthdays", "gpbf5.stan"),
include_paths = root("Birthdays"))
Data to be passed to Stan
standata5 <- list(x=data$id,
y=log(data$births_relative100),
N=length(data$id),
c_f1=1.5, # factor c of basis functions for GP for f1
M_f1=20, # number of basis functions for GP for f1
J_f2=20, # number of basis functions for periodic f2
c_g3=1.5, # factor c of basis functions for GP for g3
M_g3=5, # number of basis functions for GP for g3
scale_global=0.1, # gloval scale for RHS prior
day_of_week=data$day_of_week,
day_of_year=data$day_of_year2) # 1st March = 61 every year
Optimizing is faster than sampling (although this result can be useful in a quick workflow, the result should not be used as the final result).
opt5 <- model5$optimize(data=standata5, init=0, algorithm='lbfgs',
history=100, tol_obj=10)
Check whether parameters have reasonable values
odraws5 <- opt5$draws()
subset(odraws5, variable=c('sigma_','lengthscale_','sigma'), regex=TRUE)
# A draws_matrix: 1 iterations, 1 chains, and 7 variables
variable
draw sigma_f1 sigma_f2 sigma_g3 lengthscale_f1 lengthscale_f2 lengthscale_g3 sigma
1 9.8e-17 0.00014 1 0.00036 0.061 1.3 1
subset(odraws5, variable=c('beta_f3'))
# A draws_matrix: 1 iterations, 1 chains, and 6 variables
variable
draw beta_f3[1] beta_f3[2] beta_f3[3] beta_f3[4] beta_f3[5] beta_f3[6]
1 -8.9 -7.4 -7 -7.9 -3.5 -0.49
Ef4 <- as.numeric(subset(odraws5, variable='beta_f4'))*sd(log(data$births_relative100))
Ef4 <- exp(Ef4)*100
data.frame(x=as.Date("1988-01-01")+0:365, y=Ef4) %>%
ggplot(aes(x=x,y=y)) + geom_line(color=set1[1]) +
scale_x_date(date_breaks = "1 month", date_labels = "%b") +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births of year")
Compare the model to the data
Ef <- exp(as.numeric(subset(odraws5, variable='f')))
Ef1 <- as.numeric(subset(odraws5, variable='f1'))
Ef1 <- exp(Ef1 - mean(Ef1) + mean(log(data$births_relative100)))
Ef2 <- as.numeric(subset(odraws5, variable='f2'))
Ef2 <- exp(Ef2 - mean(Ef2) + mean(log(data$births_relative100)))
Ef_day_of_week <- as.numeric(subset(odraws5, variable='f_day_of_week'))
Ef_day_of_week <- exp(Ef_day_of_week - mean(Ef_day_of_week) + mean(log(data$births_relative100)))
Ef4 <- as.numeric(subset(odraws5, variable='beta_f4'))*sd(log(data$births_relative100))
Ef4 <- exp(Ef4)*100
pf <- data %>%
mutate(Ef = Ef) %>%
ggplot(aes(x=date, y=births_relative100)) + geom_point(color=set1[2], alpha=0.2) +
geom_line(aes(y=Ef), color=set1[1], alpha=0.75) +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births")
pf1 <- data %>%
mutate(Ef1 = Ef1) %>%
ggplot(aes(x=date, y=births_relative100)) + geom_point(color=set1[2], alpha=0.2) +
geom_line(aes(y=Ef1), color=set1[1]) +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births")
pf2 <- data %>%
mutate(Ef2 = Ef2) %>%
group_by(day_of_year2) %>%
summarise(meanbirths=mean(births_relative100), meanEf2=mean(Ef2)) %>%
ggplot(aes(x=as.Date("1987-12-31")+day_of_year2, y=meanbirths)) + geom_point(color=set1[2], alpha=0.2) +
scale_x_date(date_breaks = "1 month", date_labels = "%b") +
geom_line(aes(y=meanEf2), color=set1[1]) +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births of year")
pf3 <- ggplot(data=data, aes(x=day_of_week, y=births_relative100)) + geom_point(color=set1[2], alpha=0.2) +
scale_x_continuous(breaks = 1:7, labels=c('Mon','Tue','Wed','Thu','Fri','Sat','Sun')) +
geom_line(data=data.frame(x=1:7,y=Ef_day_of_week), aes(x=x, y=Ef_day_of_week), color=set1[1]) +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births of week")
pf2b <-data.frame(x=as.Date("1959-12-31")+1:366, y=Ef4) %>%
ggplot(aes(x=x,y=y)) + geom_line(color=set1[1]) +
scale_x_date(date_breaks = "1 month", date_labels = "%b") +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births of year")
(pf + pf1) / (pf2 + pf3) / (pf2b)
The quick model fit for model 5 is not good, but as the sampling was very slow it wasn’t easy to figure out what is going wrong.
Model 6: long term smooth + seasonal + weekday + day of year
To simplify the analysis of the day of year effect and make the inference during the exploration faster, we drop the time dependent day of week effect and RHS for a moment and use normal prior for the day of year effect.
\[
f = \mbox{intercept} + f_1 + f_2 + \beta_{\mbox{day of week}} + \beta_{\mbox{day of year}}\\
\mbox{intercept} \sim \mbox{normal}(0,1)\\
f_1 \sim \mbox{GP}(0,K_1)\\
f_2 \sim \mbox{GP}(0,K_2)\\
\beta_{\mbox{day of week}} = 0 \quad \mbox{if day of week is Monday}\\
\beta_{\mbox{day of week}} \sim \mbox{normal}(0,1) \quad \mbox{if day of week is not Monday}\\
\beta_{\mbox{day of year}} \sim \mbox{normal}(0,0.1)
\]
Compile Stan model 6 gpbf6.stan
model6 <- cmdstan_model(stan_file = root("Birthdays", "gpbf6.stan"),
include_paths = root("Birthdays"))
Data to be passed to Stan
standata6 <- list(x=data$id,
y=log(data$births_relative100),
N=length(data$id),
c_f1=1.5, # factor c of basis functions for GP for f1
M_f1=20, # number of basis functions for GP for f1
J_f2=20, # number of basis functions for periodic f2
day_of_week=data$day_of_week,
day_of_year=data$day_of_year2) # 1st March = 61 every year
Optimizing is faster than sampling (although this result can be useful in a quick workflow, the result should not be used as the final result).
opt6 <- model6$optimize(data=standata6, init=0, algorithm='lbfgs',
history=100, tol_obj=10)
Check whether parameters have reasonable values
odraws6 <- opt6$draws()
subset(odraws6, variable=c('sigma_','lengthscale_','sigma'), regex=TRUE)
# A draws_matrix: 1 iterations, 1 chains, and 6 variables
variable
draw sigma_f1 sigma_f2 sigma_f4 lengthscale_f1 lengthscale_f2 sigma
1 0.73 0.48 0.16 0.095 0.45 0.28
subset(odraws6, variable=c('beta_f3'))
# A draws_matrix: 1 iterations, 1 chains, and 6 variables
variable
draw beta_f3[1] beta_f3[2] beta_f3[3] beta_f3[4] beta_f3[5] beta_f3[6]
1 0.32 0.09 0.0085 0.14 -1.1 -1.6
Ef4 <- as.numeric(subset(odraws6, variable='beta_f4'))*sd(log(data$births_relative100))
Ef4 <- exp(Ef4)*100
data.frame(x=as.Date("1988-01-01")+0:365, y=Ef4) %>%
ggplot(aes(x=x,y=y)) + geom_line(color=set1[1]) +
scale_x_date(date_breaks = "1 month", date_labels = "%b") +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births of year")
We recognize some familiar structure in the day of year effect and proceed to sampling. Sample short chains using the early stopped optimization result as initial values (although the result from short chains can be useful in a quick workflow, the result should not be used as the final result).
init6 <- sapply(c('lengthscale_f1','lengthscale_f2',
'sigma_f1','sigma_f2','sigma_f4','sigma',
'beta_f1','beta_f2','beta_f3','beta_f4'),
function(variable) {as.numeric(subset(odraws6, variable=variable))})
fit6 <- model6$sample(data=standata6, iter_warmup=100, iter_sampling=100,
chains=4, parallel_chains=4,
init=function() { init6 })
Check whether parameters have reasonable values
draws6 <- fit6$draws()
summarise_draws(subset(draws6, variable=c('sigma_','lengthscale_','sigma'), regex=TRUE))
# A tibble: 6 × 10
variable mean median sd mad q5 q95 rhat ess_bulk ess_tail
<chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 sigma_f1 0.63 0.60 0.14 0.11 0.48 0.95 1.2 20. 44.
2 sigma_f2 0.29 0.29 0.056 0.051 0.20 0.39 1.2 30. 50.
3 sigma_f4 0.17 0.17 0.0077 0.0074 0.16 0.19 1.0 393. 374.
4 lengthscale_f1 0.21 0.21 0.034 0.035 0.16 0.27 1.3 11. 22.
5 lengthscale_f2 0.25 0.25 0.029 0.030 0.21 0.30 1.1 21. 47.
6 sigma 0.29 0.29 0.0025 0.0023 0.28 0.29 1.0 345. 258.
summarise_draws(subset(draws6, variable=c('beta_f3')))
# A tibble: 6 × 10
variable mean median sd mad q5 q95 rhat ess_bulk ess_tail
<chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 beta_f3[1] 0.35 0.35 0.012 0.013 0.33 0.37 1.0 321. 298.
2 beta_f3[2] 0.13 0.13 0.011 0.012 0.11 0.15 1.0 428. 400.
3 beta_f3[3] 0.046 0.047 0.012 0.012 0.026 0.065 1.0 410. 338.
4 beta_f3[4] 0.18 0.18 0.012 0.013 0.16 0.20 1.0 481. 305.
5 beta_f3[5] -1.1 -1.1 0.012 0.012 -1.1 -1.1 1.0 400. 415.
6 beta_f3[6] -1.5 -1.5 0.012 0.012 -1.5 -1.5 1.0 337. 448.
draws6 <- as_draws_matrix(draws6)
Ef4 <- apply(subset(draws6, variable='beta_f4'), 2, median)*sd(log(data$births_relative100))
Ef4 <- exp(Ef4)*100
data.frame(x=as.Date("1988-01-01")+0:365, y=Ef4) %>%
ggplot(aes(x=x,y=y)) + geom_line(color=set1[1]) +
scale_x_date(date_breaks = "1 month", date_labels = "%b") +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births of year")
Compare the model to the data
draws6 <- as_draws_matrix(draws6)
Ef <- exp(apply(subset(draws6, variable='f'), 2, median))
Ef1 <- apply(subset(draws6, variable='f1'), 2, median)
Ef1 <- exp(Ef1 - mean(Ef1) + mean(log(data$births_relative100)))
Ef2 <- apply(subset(draws6, variable='f2'), 2, median)
Ef2 <- exp(Ef2 - mean(Ef2) + mean(log(data$births_relative100)))
Ef_day_of_week <- apply(subset(draws6, variable='f_day_of_week'), 2, median)
Ef_day_of_week <- exp(Ef_day_of_week - mean(Ef_day_of_week) + mean(log(data$births_relative100)))
Ef4 <- apply(subset(draws6, variable='beta_f4'), 2, median)*sd(log(data$births_relative100))
Ef4 <- exp(Ef4)*100
pf <- data %>%
mutate(Ef = Ef) %>%
ggplot(aes(x=date, y=births_relative100)) + geom_point(color=set1[2], alpha=0.2) +
geom_line(aes(y=Ef), color=set1[1], alpha=0.75) +
labs(x="Date", y="Relative number of births")
pf1 <- data %>%
mutate(Ef1 = Ef1) %>%
ggplot(aes(x=date, y=births_relative100)) + geom_point(color=set1[2], alpha=0.2) +
geom_line(aes(y=Ef1), color=set1[1]) +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births")
pf2 <- data %>%
mutate(Ef2 = Ef2) %>%
group_by(day_of_year2) %>%
summarise(meanbirths=mean(births_relative100), meanEf2=mean(Ef2)) %>%
ggplot(aes(x=as.Date("1987-12-31")+day_of_year2, y=meanbirths)) + geom_point(color=set1[2], alpha=0.2) +
scale_x_date(date_breaks = "1 month", date_labels = "%b") +
geom_line(aes(y=meanEf2), color=set1[1]) +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births of year")
pf3 <- ggplot(data=data, aes(x=day_of_week, y=births_relative100)) + geom_point(color=set1[2], alpha=0.2) +
scale_x_continuous(breaks = 1:7, labels=c('Mon','Tue','Wed','Thu','Fri','Sat','Sun')) +
geom_line(data=data.frame(x=1:7,y=Ef_day_of_week), aes(x=x, y=Ef_day_of_week), color=set1[1]) +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births of week")
f13 <- data %>% filter(year==1988)%>%select(day,date)%>%mutate(y=Ef4)%>%filter(day==13)
pf2b <-data.frame(x=as.Date("1988-01-01")+0:365, y=Ef4) %>%
ggplot(aes(x=x,y=y)) + geom_line(color=set1[1]) +
scale_x_date(date_breaks = "1 month", date_labels = "%b") +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births of year") +
annotate("text",x=as.Date("1988-01-01"),y=Ef4[1]-1,label="New year") +
annotate("text",x=as.Date("1988-02-14"),y=Ef4[45]+1.5,label="Valentine's day") +
annotate("text",x=as.Date("1988-02-29"),y=Ef4[60]-2.5,label="Leap day") +
annotate("text",x=as.Date("1988-04-01"),y=Ef4[92]-1.5,label="April 1st") +
annotate("text",x=as.Date("1988-07-04"),y=Ef4[186]-1.5,label="Independence day") +
annotate("text",x=as.Date("1988-10-31"),y=Ef4[305]-1.5,label="Halloween") +
annotate("text",x=as.Date("1988-12-24"),y=Ef4[360]-1.5,label="Christmas") +
geom_point(data=f13,aes(x=date,y=y), size=3, shape=1)
(pf + pf1) / (pf2 + pf3) / pf2b
The short sampling result looks reasonable and thus the problem is not in adding the day of year effect itself. In the bottom plot, the circles mark 13th day of each month. Results look similar to our previous analyses , so it seems the day or year effect model component is working as it should, but there was some problem with our RHS implementation. As there is more variation in the day of year effects than we would hope, we did some additional experiments with different priors for the day of year effect (double exponential, Cauchy and Student’s t with unknown degrees of freedom as models 6b, 6c, 6d), but decided it’s better to add other components before investing that part more thoroughly.
Model 7: long term smooth + seasonal + weekday + day of year normal + floating special days
We can see in the model 6 results that day of year effects have some dips in the relative number of births that are spread over a week. From previous analyse we know these correspond to holidays that are not on a specific day of year, but are for example on the last Monday of May. We call these floating special days and include Memorial day (last Monday of May), Labor day (first Monday of September, and we include also the following Tuesday), and Thanksgiving (fourth Thursday of November, and we include also the following Friday).
Compile Stan model 7 gpbf7.stan
model7 <- cmdstan_model(stan_file = root("Birthdays", "gpbf7.stan"),
include_paths = root("Birthdays"))
Floating special days
# Memorial day
memorial_days <- with(data,which(month==5&day_of_week==1&day>=25))
# Labor day
labor_days <- with(data,which(month==9&day_of_week==1&day<=7))
labor_days <- c(labor_days, labor_days+1)
# Thanksgiving
thanksgiving_days <- with(data,which(month==11&day_of_week==4&day>=22&day<=28))
thanksgiving_days <- c(thanksgiving_days, thanksgiving_days+1)
Data to be passed to Stan
standata7 <- list(x=data$id,
y=log(data$births_relative100),
N=length(data$id),
c_f1=1.5, # factor c of basis functions for GP for f1
M_f1=20, # number of basis functions for GP for f1
J_f2=20, # number of basis functions for periodic f2
day_of_week=data$day_of_week,
day_of_year=data$day_of_year2, # 1st March = 61 every year
memorial_days=memorial_days,
labor_days=labor_days,
thanksgiving_days=thanksgiving_days)
Optimizing is faster than sampling (although this result can be useful in a quick workflow, the result should not be used as the final result).
opt7 <- model7$optimize(data=standata7, init=0, algorithm='lbfgs',
history=100, tol_obj=10)
Check whether parameters have reasonable values
odraws7 <- opt7$draws()
subset(odraws7, variable=c('intercept','sigma_','lengthscale_','sigma'), regex=TRUE)
# A draws_matrix: 1 iterations, 1 chains, and 6 variables
variable
draw sigma_f1 sigma_f2 sigma_f4 lengthscale_f1 lengthscale_f2 sigma
1 0.83 0.44 0.16 0.063 0.43 0.26
subset(odraws7, variable=c('beta_f3'))
# A draws_matrix: 1 iterations, 1 chains, and 6 variables
variable
draw beta_f3[1] beta_f3[2] beta_f3[3] beta_f3[4] beta_f3[5] beta_f3[6]
1 0.28 0.056 0.0099 0.12 -1.2 -1.6
Ef4 <- as.numeric(subset(odraws7, variable='beta_f4'))*sd(log(data$births_relative100))
Ef4 <- exp(Ef4)*100
data.frame(x=as.Date("1988-01-01")+0:365, y=Ef4) %>%
ggplot(aes(x=x,y=y)) + geom_line(color=set1[1]) +
scale_x_date(date_breaks = "1 month", date_labels = "%b") +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births of year")
Sample short chains using the early stopped optimization result as initial values (although the result from short chains can be useful in a quick workflow, the result should not be used as the final result).
init7 <- sapply(c('lengthscale_f1','lengthscale_f2',
'sigma_f1','sigma_f2','sigma_f4','sigma',
'beta_f1','beta_f2','beta_f3','beta_f4','beta_f5'),
function(variable) {as.numeric(subset(odraws7, variable=variable))})
fit7 <- model7$sample(data=standata7, iter_warmup=100, iter_sampling=100, chains=4, parallel_chains=4,
init=function() { init7 }, refresh=10)
Check whether parameters have reasonable values
draws7 <- fit7$draws()
summarise_draws(subset(draws7, variable=c('sigma_','lengthscale_','sigma'), regex=TRUE))
# A tibble: 6 × 10
variable mean median sd mad q5 q95 rhat ess_bulk ess_tail
<chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 sigma_f1 0.73 0.72 0.14 0.12 0.53 1.0 1.5 7.9 22.
2 sigma_f2 0.28 0.28 0.038 0.028 0.23 0.36 1.2 16. 27.
3 sigma_f4 0.17 0.17 0.0079 0.0079 0.16 0.19 1.0 407. 271.
4 lengthscale_f1 0.22 0.22 0.031 0.025 0.15 0.26 1.6 7.1 12.
5 lengthscale_f2 0.31 0.30 0.039 0.041 0.24 0.38 1.5 7.7 30.
6 sigma 0.26 0.26 0.0021 0.0020 0.26 0.27 1.0 484. 234.
summarise_draws(subset(draws7, variable=c('beta_f3')))
# A tibble: 6 × 10
variable mean median sd mad q5 q95 rhat ess_bulk ess_tail
<chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 beta_f3[1] 0.31 0.31 0.012 0.012 0.29 0.32 1.0 510. 329.
2 beta_f3[2] 0.074 0.074 0.011 0.011 0.056 0.092 1.0 475. 333.
3 beta_f3[3] 0.029 0.029 0.011 0.011 0.012 0.047 1.0 399. 262.
4 beta_f3[4] 0.14 0.14 0.011 0.012 0.12 0.16 1.0 461. 326.
5 beta_f3[5] -1.2 -1.2 0.011 0.011 -1.2 -1.1 1.0 462. 358.
6 beta_f3[6] -1.6 -1.6 0.011 0.011 -1.6 -1.6 1.0 471. 411.
Compare the model to the data
draws7 <- as_draws_matrix(draws7)
Ef <- exp(apply(subset(draws7, variable='f'), 2, median))
Ef1 <- apply(subset(draws7, variable='f1'), 2, median)
Ef1 <- exp(Ef1 - mean(Ef1) + mean(log(data$births_relative100)))
Ef2 <- apply(subset(draws7, variable='f2'), 2, median)
Ef2 <- exp(Ef2 - mean(Ef2) + mean(log(data$births_relative100)))
Ef_day_of_week <- apply(subset(draws7, variable='f_day_of_week'), 2, median)
Ef_day_of_week <- exp(Ef_day_of_week - mean(Ef_day_of_week) + mean(log(data$births_relative100)))
Ef4 <- apply(subset(draws7, variable='beta_f4'), 2, median)*sd(log(data$births_relative100))
Ef4 <- exp(Ef4)*100
Efloats <- apply(subset(draws7, variable='beta_f5'), 2, median)*sd(log(data$births_relative100))
Efloats <- exp(Efloats)*100
floats1988<-c(memorial_days[20], labor_days[c(20,40)], thanksgiving_days[c(20,40)])-6939
Ef4float <- Ef4
Ef4float[floats1988] <- Ef4float[floats1988]*Efloats[c(1,2,2,3,3)]/100
pf <- data %>%
mutate(Ef = Ef) %>%
ggplot(aes(x=date, y=births_relative100)) + geom_point(color=set1[2], alpha=0.2) +
geom_line(aes(y=Ef), color=set1[1], alpha=0.75) +
labs(x="Date", y="Relative number of births")
pf1 <- data %>%
mutate(Ef1 = Ef1) %>%
ggplot(aes(x=date, y=births_relative100)) + geom_point(color=set1[2], alpha=0.2) +
geom_line(aes(y=Ef1), color=set1[1]) +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births")
pf2 <- data %>%
mutate(Ef2 = Ef2) %>%
group_by(day_of_year2) %>%
summarise(meanbirths=mean(births_relative100), meanEf2=mean(Ef2)) %>%
ggplot(aes(x=as.Date("1987-12-31")+day_of_year2, y=meanbirths)) + geom_point(color=set1[2], alpha=0.2) +
scale_x_date(date_breaks = "1 month", date_labels = "%b") +
geom_line(aes(y=meanEf2), color=set1[1]) +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births of year")
pf3 <- ggplot(data=data, aes(x=day_of_week, y=births_relative100)) + geom_point(color=set1[2], alpha=0.2) +
scale_x_continuous(breaks = 1:7, labels=c('Mon','Tue','Wed','Thu','Fri','Sat','Sun')) +
geom_line(data=data.frame(x=1:7,y=Ef_day_of_week), aes(x=x, y=Ef_day_of_week), color=set1[1]) +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births of week")
f13 <- data %>% filter(year==1988)%>%select(day,date)%>%mutate(y=Ef4float)%>%filter(day==13)
pf2b <-data.frame(x=as.Date("1988-01-01")+0:365, y=Ef4float) %>%
ggplot(aes(x=x,y=y)) + geom_line(color=set1[1]) +
scale_x_date(date_breaks = "1 month", date_labels = "%b") +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births of year") +
annotate("text",x=as.Date("1988-01-01"),y=Ef4float[1]-1,label="New year") +
annotate("text",x=as.Date("1988-02-14"),y=Ef4float[45]+1.5,label="Valentine's day") +
annotate("text",x=as.Date("1988-02-29"),y=Ef4float[60]-2.5,label="Leap day") +
annotate("text",x=as.Date("1988-04-01"),y=Ef4float[92]-1.5,label="April 1st") +
annotate("text",x=as.Date("1988-07-04"),y=Ef4float[186]-1.5,label="Independence day") +
annotate("text",x=as.Date("1988-10-31"),y=Ef4float[305]-1.5,label="Halloween") +
annotate("text",x=as.Date("1988-12-24"),y=Ef4float[360]-2,label="Christmas") +
annotate("text",x=as.Date("1988-05-30"),y=Ef4float[151]-1.5,label="Memorial day") +
annotate("text",x=as.Date("1988-09-05"),y=Ef4float[249]-1.5,label="Labor day") +
annotate("text",x=as.Date("1988-11-24"),y=Ef4float[329]-1,label="Thanksgiving")+
geom_point(data=f13,aes(x=date,y=y), size=3, shape=1)
(pf + pf1) / (pf2 + pf3) / (pf2b)
The day of year and floating special day effects are shown for year 1988 (which is also a leap year) and the results seem reasonable.
Model 8: long term smooth + seasonal + weekday with time dependent magnitude + day of year + special
As the day of year and floating day effects work well, we’ll add the time dependent day of week effect back to the model.
Compile Stan model 8 gpbf8.stan
model8 <- cmdstan_model(stan_file = root("Birthdays", "gpbf8.stan"),
include_paths = root("Birthdays"))
Floating special days
# Memorial day
memorial_days <- with(data,which(month==5&day_of_week==1&day>=25))
# Labor day
labor_days <- with(data,which(month==9&day_of_week==1&day<=7))
labor_days <- c(labor_days, labor_days+1)
# Thanksgiving
thanksgiving_days <- with(data,which(month==11&day_of_week==4&day>=22&day<=28))
thanksgiving_days <- c(thanksgiving_days, thanksgiving_days+1)
Data to be passed to Stan
standata8 <- list(x=data$id,
y=log(data$births_relative100),
N=length(data$id),
c_f1=1.5, # factor c of basis functions for GP for f1
M_f1=20, # number of basis functions for GP for f1
J_f2=20, # number of basis functions for periodic f2
c_g3=1.5, # factor c of basis functions for GP for g3
M_g3=5, # number of basis functions for GP for g3
day_of_week=data$day_of_week,
day_of_year=data$day_of_year2, # 1st March = 61 every year
memorial_days=memorial_days,
labor_days=labor_days,
thanksgiving_days=thanksgiving_days)
Optimizing is faster than sampling (although this result can be useful in a quick workflow, the result should not be used as the final result).
opt8 <- model8$optimize(data=standata8, init=0.1, algorithm='lbfgs',
history=100, tol_obj=10)
Check whether parameters have reasonable values
odraws8 <- opt8$draws()
subset(odraws8, variable=c('sigma_','lengthscale_','sigma'), regex=TRUE)
# A draws_matrix: 1 iterations, 1 chains, and 8 variables
variable
draw sigma_f1 sigma_f2 sigma_g3 sigma_f4 lengthscale_f1 lengthscale_f2 lengthscale_g3 sigma
1 0.51 0.57 0.4 0.18 0.18 0.45 0.83 0.23
subset(odraws8, variable=c('beta_f3'))
# A draws_matrix: 1 iterations, 1 chains, and 6 variables
variable
draw beta_f3[1] beta_f3[2] beta_f3[3] beta_f3[4] beta_f3[5] beta_f3[6]
1 0.36 0.1 0.055 0.18 -1.4 -1.8
Ef4 <- as.numeric(subset(odraws8, variable='beta_f4'))*sd(log(data$births_relative100))
Ef4 <- exp(Ef4)*100
data.frame(x=as.Date("1988-01-01")+0:365, y=Ef4) %>%
ggplot(aes(x=x,y=y)) + geom_line(color=set1[1]) +
scale_x_date(date_breaks = "1 month", date_labels = "%b") +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births of year")
Compare the model to the data
Ef <- exp(as.numeric(subset(odraws8, variable='f')))
Ef1 <- as.numeric(subset(odraws8, variable='f1'))
Ef1 <- exp(Ef1 - mean(Ef1) + mean(log(data$births_relative100)))
Ef2 <- as.numeric(subset(odraws8, variable='f2'))
Ef2 <- exp(Ef2 - mean(Ef2) + mean(log(data$births_relative100)))
Ef_day_of_week <- as.numeric(subset(odraws8, variable='f_day_of_week'))
Ef_day_of_week <- exp(Ef_day_of_week - mean(Ef_day_of_week) + mean(log(data$births_relative100)))
Ef3 <- as.numeric(subset(odraws8, variable='f3'))
Ef3 <- exp(Ef3 - mean(Ef3) + mean(log(data$births_relative100)))
Ef4 <- as.numeric(subset(odraws8, variable='beta_f4'))*sd(log(data$births_relative100))
Ef4 <- exp(Ef4)*100
Efloats <- as.numeric(subset(odraws8, variable='beta_f5'))*sd(log(data$births_relative100))
Efloats <- exp(Efloats)*100
floats1988<-c(memorial_days[20], labor_days[c(20,40)], thanksgiving_days[c(20,40)])-6939
Ef4float <- Ef4
Ef4float[floats1988] <- Ef4float[floats1988]*Efloats[c(1,2,2,3,3)]/100
pf <- data %>%
mutate(Ef = Ef) %>%
ggplot(aes(x=date, y=births_relative100)) + geom_point(color=set1[2], alpha=0.2) +
geom_point(aes(y=Ef), color=set1[1], alpha=0.2) +
labs(x="Date", y="Relative number of births")
pf1 <- data %>%
mutate(Ef1 = Ef1) %>%
ggplot(aes(x=date, y=births_relative100)) + geom_point(color=set1[2], alpha=0.2) +
geom_line(aes(y=Ef1), color=set1[1]) +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births")
pf2 <- data %>%
mutate(Ef2 = Ef2) %>%
group_by(day_of_year2) %>%
summarise(meanbirths=mean(births_relative100), meanEf2=mean(Ef2)) %>%
ggplot(aes(x=as.Date("1987-12-31")+day_of_year2, y=meanbirths)) + geom_point(color=set1[2], alpha=0.2) +
scale_x_date(date_breaks = "1 month", date_labels = "%b") +
geom_line(aes(y=meanEf2), color=set1[1]) +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births of year")
pf3 <- ggplot(data=data, aes(x=day_of_week, y=births_relative100)) + geom_point(color=set1[2], alpha=0.2) +
scale_x_continuous(breaks = 1:7, labels=c('Mon','Tue','Wed','Thu','Fri','Sat','Sun')) +
geom_line(data=data.frame(x=1:7,y=Ef_day_of_week), aes(x=x, y=Ef_day_of_week), color=set1[1]) +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births of week")
N=length(data$id)
pf3b <- data %>%
mutate(Ef3 = Ef3*Ef1/100) %>%
ggplot(aes(x=date, y=births_relative100)) + geom_point(color=set1[2], alpha=0.2) +
geom_point(aes(y=Ef3), color=set1[1], size=0.1) +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births") +
annotate("text",x=as.Date("1989-08-01"),y=(Ef3*Ef1/100)[c((N-5):(N-4), N, N-6)],label=c("Mon","Tue","Sat","Sun"))
f13 <- data %>% filter(year==1988)%>%select(day,date)%>%mutate(y=Ef4float)%>%filter(day==13)
pf2b <-data.frame(x=as.Date("1988-01-01")+0:365, y=Ef4float) %>%
ggplot(aes(x=x,y=y)) + geom_line(color=set1[1]) +
scale_x_date(date_breaks = "1 month", date_labels = "%b") +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births of year") +
annotate("text",x=as.Date("1988-01-01"),y=Ef4float[1]-1,label="New year") +
annotate("text",x=as.Date("1988-02-14"),y=Ef4float[45]+1.5,label="Valentine's day") +
annotate("text",x=as.Date("1988-02-29"),y=Ef4float[60]-2.5,label="Leap day") +
annotate("text",x=as.Date("1988-04-01"),y=Ef4float[92]-1.5,label="April 1st") +
annotate("text",x=as.Date("1988-07-04"),y=Ef4float[186]-1.5,label="Independence day") +
annotate("text",x=as.Date("1988-10-31"),y=Ef4float[305]-1.5,label="Halloween") +
annotate("text",x=as.Date("1988-12-24"),y=Ef4float[360]-2,label="Christmas") +
annotate("text",x=as.Date("1988-05-30"),y=Ef4float[151]-2,label="Memorial day") +
annotate("text",x=as.Date("1988-09-05"),y=Ef4float[249]-1.5,label="Labor day") +
annotate("text",x=as.Date("1988-11-24"),y=Ef4float[329]-1,label="Thanksgiving")+
geom_point(data=f13,aes(x=date,y=y), size=3, shape=1)
(pf + pf1) / (pf2 + pf3b) / (pf2b)
Sample short chains using the early stopped optimization result as initial values (although the result from short chains can be useful in a quick workflow, the result should not be used as the final result).
init8 <- sapply(c('lengthscale_f1','lengthscale_f2','lengthscale_g3',
'sigma_f1','sigma_f2','sigma_g3','sigma_f4','sigma',
'beta_f1','beta_f2','beta_f3','beta_g3','beta_f4','beta_f5'),
function(variable) {as.numeric(subset(odraws8, variable=variable))})
fit8 <- model8$sample(data=standata8, iter_warmup=100, iter_sampling=100, chains=4, parallel_chains=4,
init=function() { init8 }, refresh=10)
Check whether parameters have reasonable values
draws8 <- fit8$draws()
summarise_draws(subset(draws8, variable=c('sigma_','lengthscale_','sigma'), regex=TRUE))
# A tibble: 8 × 10
variable mean median sd mad q5 q95 rhat ess_bulk ess_tail
<chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 sigma_f1 0.73 0.70 0.13 0.12 0.59 0.97 1.6 7.0 23.
2 sigma_f2 0.30 0.30 0.035 0.040 0.24 0.35 1.3 14. 32.
3 sigma_g3 0.18 0.17 0.043 0.030 0.13 0.28 1.4 9.6 28.
4 sigma_f4 0.17 0.17 0.0077 0.0078 0.16 0.19 1.0 788. 238.
5 lengthscale_f1 0.22 0.23 0.031 0.038 0.17 0.27 1.9 6.2 30.
6 lengthscale_f2 0.31 0.31 0.029 0.029 0.26 0.36 1.3 13. 70.
7 lengthscale_g3 0.62 0.61 0.17 0.22 0.36 0.86 1.2 13. 118.
8 sigma 0.23 0.23 0.0020 0.0021 0.23 0.24 1.0 202. 163.
summarise_draws(subset(draws8, variable=c('beta_f3')))
# A tibble: 6 × 10
variable mean median sd mad q5 q95 rhat ess_bulk ess_tail
<chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 beta_f3[1] 0.31 0.31 0.023 0.025 0.28 0.35 1.7 6.7 20.
2 beta_f3[2] 0.082 0.082 0.011 0.011 0.065 0.10 1.1 21. 62.
3 beta_f3[3] 0.043 0.042 0.011 0.011 0.026 0.061 1.0 145. 368.
4 beta_f3[4] 0.15 0.15 0.014 0.014 0.13 0.17 1.3 11. 46.
5 beta_f3[5] -1.2 -1.2 0.078 0.091 -1.3 -1.1 2.1 5.8 24.
6 beta_f3[6] -1.6 -1.6 0.10 0.12 -1.8 -1.4 2.1 5.7 20.
Compare the model to the data
draws8 <- as_draws_matrix(draws8)
Ef <- exp(apply(subset(draws8, variable='f'), 2, median))
Ef1 <- apply(subset(draws8, variable='f1'), 2, median)
Ef1 <- exp(Ef1 - mean(Ef1) + mean(log(data$births_relative100)))
Ef2 <- apply(subset(draws8, variable='f2'), 2, median)
Ef2 <- exp(Ef2 - mean(Ef2) + mean(log(data$births_relative100)))
Ef_day_of_week <- apply(subset(draws8, variable='f_day_of_week'), 2, median)
Ef_day_of_week <- exp(Ef_day_of_week - mean(Ef_day_of_week) + mean(log(data$births_relative100)))
Ef3 <- apply(subset(draws8, variable='f3'), 2, median)
Ef3 <- exp(Ef3 - mean(Ef3) + mean(log(data$births_relative100)))
Ef4 <- apply(subset(draws8, variable='beta_f4'), 2, median)*sd(log(data$births_relative100))
Ef4 <- exp(Ef4)*100
Efloats <- apply(subset(draws8, variable='beta_f5'), 2, median)*sd(log(data$births_relative100))
Efloats <- exp(Efloats)*100
floats1988<-c(memorial_days[20], labor_days[c(20,40)], thanksgiving_days[c(20,40)])-6939
Ef4float <- Ef4
Ef4float[floats1988] <- Ef4float[floats1988]*Efloats[c(1,2,2,3,3)]/100
pf <- data %>%
mutate(Ef = Ef) %>%
ggplot(aes(x=date, y=births_relative100)) + geom_point(color=set1[2], alpha=0.2) +
geom_point(aes(y=Ef), color=set1[1], alpha=0.2) +
labs(x="Date", y="Relative number of births")
pf1 <- data %>%
mutate(Ef1 = Ef1) %>%
ggplot(aes(x=date, y=births_relative100)) + geom_point(color=set1[2], alpha=0.2) +
geom_line(aes(y=Ef1), color=set1[1]) +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births")
pf2 <- data %>%
mutate(Ef2 = Ef2) %>%
group_by(day_of_year2) %>%
summarise(meanbirths=mean(births_relative100), meanEf2=mean(Ef2)) %>%
ggplot(aes(x=as.Date("1987-12-31")+day_of_year2, y=meanbirths)) + geom_point(color=set1[2], alpha=0.2) +
scale_x_date(date_breaks = "1 month", date_labels = "%b") +
geom_line(aes(y=meanEf2), color=set1[1]) +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births of year")
pf3 <- ggplot(data=data, aes(x=day_of_week, y=births_relative100)) + geom_point(color=set1[2], alpha=0.2) +
scale_x_continuous(breaks = 1:7, labels=c('Mon','Tue','Wed','Thu','Fri','Sat','Sun')) +
geom_line(data=data.frame(x=1:7,y=Ef_day_of_week), aes(x=x, y=Ef_day_of_week), color=set1[1]) +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births of week")
N=length(data$id)
pf3b <- data %>%
mutate(Ef3 = Ef3*Ef1/100) %>%
ggplot(aes(x=date, y=births_relative100)) + geom_point(color=set1[2], alpha=0.2) +
geom_point(aes(y=Ef3), color=set1[1], size=0.1) +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births") +
annotate("text",x=as.Date("1989-08-01"),y=(Ef3*Ef1/100)[c((N-5):(N-4), N, N-6)],label=c("Mon","Tue","Sat","Sun"))
f13 <- data %>% filter(year==1988)%>%select(day,date)%>%mutate(y=Ef4float)%>%filter(day==13)
pf2b <-data.frame(x=as.Date("1988-01-01")+0:365, y=Ef4float) %>%
ggplot(aes(x=x,y=y)) + geom_line(color=set1[1]) +
scale_x_date(date_breaks = "1 month", date_labels = "%b") +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births of year") +
annotate("text",x=as.Date("1988-01-01"),y=Ef4float[1]-1,label="New year") +
annotate("text",x=as.Date("1988-02-14"),y=Ef4float[45]+1.5,label="Valentine's day") +
annotate("text",x=as.Date("1988-02-29"),y=Ef4float[60]-2.5,label="Leap day") +
annotate("text",x=as.Date("1988-04-01"),y=Ef4float[92]-1.5,label="April 1st") +
annotate("text",x=as.Date("1988-07-04"),y=Ef4float[186]-1.5,label="Independence day") +
annotate("text",x=as.Date("1988-10-31"),y=Ef4float[305]-1.5,label="Halloween") +
annotate("text",x=as.Date("1988-12-24"),y=Ef4float[360]-2,label="Christmas") +
annotate("text",x=as.Date("1988-05-30"),y=Ef4float[151]-2,label="Memorial day") +
annotate("text",x=as.Date("1988-09-05"),y=Ef4float[249]-1.5,label="Labor day") +
annotate("text",x=as.Date("1988-11-24"),y=Ef4float[329]-1,label="Thanksgiving")+
geom_point(data=f13,aes(x=date,y=y), size=3, shape=1)
(pf + pf1) / (pf2 + pf3b) / (pf2b)
The inference for the model works fine, which hints that our RHS implementation for the model 5 was wrong or had very difficult posterior. Before testing RHS again, we’ll test with an easier to implement Student’s \(t\) prior whether long tailed prior for day of year effect is reasonable. These experiments help also to find out whether the day of year effect is sensitive to the prior choice.
Model 8+t_nu: day of year effect with Student’s t prior
Compile Stan model 8 + t_nu gpbf8tnu.stan
model8tnu <- cmdstan_model(stan_file = root("Birthdays", "gpbf8tnu.stan"),
include_paths = root("Birthdays"))
Optimizing is faster than sampling (although this result can be useful in a quick workflow, the result should not be used as the final result).
opt8tnu <- model8tnu$optimize(data=standata8, init=0.1, algorithm='lbfgs',
history=100, tol_obj=10)
odraws8tnu <- opt8tnu$draws()
Sample short chains using the early stopped optimization result as initial values (although the result from short chains can be useful in a quick workflow, the result should not be used as the final result).
init8tnu <- sapply(c('lengthscale_f1','lengthscale_f2','lengthscale_g3',
'sigma_f1','sigma_f2','sigma_g3','sigma_f4','nu_f4','sigma',
'beta_f1','beta_f2','beta_f3','beta_g3','beta_f4','beta_f5'),
function(variable) {as.numeric(subset(odraws8tnu, variable=variable))})
fit8tnu <- model8tnu$sample(data=standata8, iter_warmup=100, iter_sampling=100,
chains=4, parallel_chains=4,
init=function() { init8tnu }, refresh=10)
Check whether parameters have reasonable values
draws8tnu <- fit8tnu$draws()
summarise_draws(subset(draws8tnu, variable=c('intercept','sigma_','lengthscale_','sigma','nu_'), regex=TRUE))
# A tibble: 9 × 10
variable mean median sd mad q5 q95 rhat ess_bulk ess_tail
<chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 sigma_f1 0.71 0.70 0.16 0.18 0.51 1.0 1.2 19. 83.
2 sigma_f2 0.29 0.28 0.046 0.039 0.23 0.40 1.2 27. 15.
3 sigma_g3 0.21 0.21 0.050 0.043 0.14 0.31 1.1 70. 122.
4 sigma_f4 0.0039 0.0034 0.0016 0.0017 0.0019 0.0069 2.1 5.7 40.
5 lengthscale_f1 0.22 0.22 0.034 0.026 0.15 0.26 1.1 22. 75.
6 lengthscale_f2 0.21 0.20 0.014 0.013 0.18 0.23 1.3 10. 56.
7 lengthscale_g3 0.75 0.75 0.21 0.22 0.40 1.1 1.1 25. 98.
8 sigma 0.23 0.23 0.0021 0.0021 0.23 0.24 1.0 315. 171.
9 nu_f4 0.74 0.72 0.11 0.11 0.58 0.92 1.3 11. 78.
Posterior of degrees of freedom nu_f4
is very close to 0.5, and thus the distribution has thicker tails than Cauchy. This is strong evidence that the distribution of day of year effects is far from normal. Compare the model to the data
draws8 <- as_draws_matrix(draws8tnu)
Ef <- exp(apply(subset(draws8, variable='f'), 2, median))
Ef1 <- apply(subset(draws8, variable='f1'), 2, median)
Ef1 <- exp(Ef1 - mean(Ef1) + mean(log(data$births_relative100)))
Ef2 <- apply(subset(draws8, variable='f2'), 2, median)
Ef2 <- exp(Ef2 - mean(Ef2) + mean(log(data$births_relative100)))
Ef_day_of_week <- apply(subset(draws8, variable='f_day_of_week'), 2, median)
Ef_day_of_week <- exp(Ef_day_of_week - mean(Ef_day_of_week) + mean(log(data$births_relative100)))
Ef3 <- apply(subset(draws8, variable='f3'), 2, median)
Ef3 <- exp(Ef3 - mean(Ef3) + mean(log(data$births_relative100)))
Ef4 <- apply(subset(draws8, variable='beta_f4'), 2, median)*sd(log(data$births_relative100))
Ef4 <- exp(Ef4)*100
Efloats <- apply(subset(draws8, variable='beta_f5'), 2, median)*sd(log(data$births_relative100))
Efloats <- exp(Efloats)*100
floats1988<-c(memorial_days[20], labor_days[c(20,40)], thanksgiving_days[c(20,40)])-6939
Ef4float <- Ef4
Ef4float[floats1988] <- Ef4float[floats1988]*Efloats[c(1,2,2,3,3)]/100
pf <- data %>%
mutate(Ef = Ef) %>%
ggplot(aes(x=date, y=births_relative100)) + geom_point(color=set1[2], alpha=0.2) +
geom_point(aes(y=Ef), color=set1[1], alpha=0.2) +
labs(x="Date", y="Relative number of births")
pf1 <- data %>%
mutate(Ef1 = Ef1) %>%
ggplot(aes(x=date, y=births_relative100)) + geom_point(color=set1[2], alpha=0.2) +
geom_line(aes(y=Ef1), color=set1[1]) +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births")
pf2 <- data %>%
mutate(Ef2 = Ef2) %>%
group_by(day_of_year2) %>%
summarise(meanbirths=mean(births_relative100), meanEf2=mean(Ef2)) %>%
ggplot(aes(x=as.Date("1987-12-31")+day_of_year2, y=meanbirths)) + geom_point(color=set1[2], alpha=0.2) +
scale_x_date(date_breaks = "1 month", date_labels = "%b") +
geom_line(aes(y=meanEf2), color=set1[1]) +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births of year")
pf3 <- ggplot(data=data, aes(x=day_of_week, y=births_relative100)) + geom_point(color=set1[2], alpha=0.2) +
scale_x_continuous(breaks = 1:7, labels=c('Mon','Tue','Wed','Thu','Fri','Sat','Sun')) +
geom_line(data=data.frame(x=1:7,y=Ef_day_of_week), aes(x=x, y=Ef_day_of_week), color=set1[1]) +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births of week")
N=length(data$id)
pf3b <- data %>%
mutate(Ef3 = Ef3*Ef1/100) %>%
ggplot(aes(x=date, y=births_relative100)) + geom_point(color=set1[2], alpha=0.2) +
geom_point(aes(y=Ef3), color=set1[1], size=0.1) +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births") +
annotate("text",x=as.Date("1989-08-01"),y=(Ef3*Ef1/100)[c((N-5):(N-4), N, N-6)],label=c("Mon","Tue","Sat","Sun"))
f13 <- data %>% filter(year==1988)%>%select(day,date)%>%mutate(y=Ef4float)%>%filter(day==13)
pf2b <-data.frame(x=as.Date("1988-01-01")+0:365, y=Ef4float) %>%
ggplot(aes(x=x,y=y)) + geom_line(color=set1[1]) +
scale_x_date(date_breaks = "1 month", date_labels = "%b") +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births of year") +
annotate("text",x=as.Date("1988-01-01"),y=Ef4float[1]-1,label="New year") +
annotate("text",x=as.Date("1988-02-14"),y=Ef4float[45]+1.5,label="Valentine's day") +
annotate("text",x=as.Date("1988-02-29"),y=Ef4float[60]-2.5,label="Leap day") +
annotate("text",x=as.Date("1988-04-01"),y=Ef4float[92]-1.5,label="April 1st") +
annotate("text",x=as.Date("1988-07-04"),y=Ef4float[186]-1.5,label="Independence day") +
annotate("text",x=as.Date("1988-10-31"),y=Ef4float[305]-1.5,label="Halloween") +
annotate("text",x=as.Date("1988-12-24"),y=Ef4float[360]-2,label="Christmas") +
annotate("text",x=as.Date("1988-05-30"),y=Ef4float[151]-2,label="Memorial day") +
annotate("text",x=as.Date("1988-09-05"),y=Ef4float[249]-1.5,label="Labor day") +
annotate("text",x=as.Date("1988-11-24"),y=Ef4float[329]-1,label="Thanksgiving")+
geom_point(data=f13,aes(x=date,y=y), size=3, shape=1)
(pf + pf1) / (pf2 + pf3b) / (pf2b)
The other effects seem to be quite similar as with the previous model, but the day of year effects are clearly different with most days having non-detectable effect. There are also effects that seemed to be quite clear in normal prior model such as 13th day of month effect, which is not visible anymore. As the posterior of degrees of freedom t_nu
was concentrated close to 1, it’s likely that the normal prior for day of year effect can’t be the best. So far we hadn’t used model comparison such as leave-one-out cross-validation (LOO-CV) as each added component had qualitatively big and reasonable effect. Now as day of year effect is sensitive to prior choice, but it’s not clear how much better \(t_\nu\) prior distribution is we use LOO-CV to compare the models.
loo8 <- fit8$loo()
loo8tnu <- fit8tnu$loo()
loo_compare(list(`Model 8 normal`=loo8,`Model 8 Student\'s t`=loo8tnu))
elpd_diff se_diff
Model 8 Student's t 0.0 0.0
Model 8 normal -116.4 16.4
As we could have expected based on the posterior of nu_f4
Student’s t prior on day of year effects is better. As low degrees of freedom indicate a thick tailed distribution for day of year effect is needed, we decided to test again RHS prior.
Model 8+RHS: day of year effect with RHS prior
Model 5 had RHS prior but the problem was that optimization result wasn’t even close to sensible and MCMC was very slow. Given the other models we now know that the problem is not in adding day of year effect or combining it with time dependent magnitude for the day of week effect. It was easier now to focus on figuring out the problem in RHS. Since RHS is presented as a scale mixture of normals involving hierarchical prior, it is common to use non-centered parameterization for RHS prior. Non-centered parameterization is useful when the information from the likelihood is weak and the prior dependency dominates in the posterior dependency. RHS is often used when there are less observations than unknowns. In this problem each unknown (one day of year effect) is informed by several observations from different years, and then it might be that the centered parameterization is better. And this turned out to be true and the inference for model 8 with centered parameterization RHS prior on day of year effect worked much better than for model 5. (In Stan it was easy to test switch from non-centered to centered parameterization by removing the multplier from one of the parameter declarations).
Compile Stan model 8 + RHS gpbf8rhs.stan
model8rhs <- cmdstan_model(stan_file = root("Birthdays", "gpbf8rhs.stan"),
include_paths = root("Birthdays"))
Add a global scale for RHS prior
standata8 <- c(standata8,
scale_global=0.1) # global scale for RHS prior
Optimizing is faster than sampling (although this result can be useful in a quick workflow, the result should not be used as the final result).
opt8rhs <- model8rhs$optimize(data=standata8, init=0.1, algorithm='lbfgs',
history=100, tol_obj=10)
odraws8rhs <- opt8rhs$draws()
Sample short chains using the optimization result as initial values (although the result from short chains can be useful in a quick workflow, the result should not be used as the final result).
init8rhs <- sapply(c('lengthscale_f1','lengthscale_f2','lengthscale_g3',
'sigma_f1','sigma_f2','sigma_g3','sigma_f4','sigma',
'beta_f1','beta_f2','beta_f3','beta_g3','beta_f4','beta_f5',
'tau_f4','lambda_f4','caux_f4'),
function(variable) {as.numeric(subset(odraws8rhs, variable=variable))})
fit8rhs <- model8rhs$sample(data=standata8, iter_warmup=100, iter_sampling=100,
chains=4, parallel_chains=4,
init=function() { init8rhs }, refresh=10)
Check whether parameters have reasonable values
draws8rhs <- fit8rhs$draws()
summarise_draws(subset(draws8rhs, variable=c('sigma_','lengthscale_','sigma','nu_'), regex=TRUE))
# A tibble: 8 × 10
variable mean median sd mad q5 q95 rhat ess_bulk ess_tail
<chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 sigma_f1 0.71 0.72 0.11 0.13 0.52 0.88 1.6 7.1 50.
2 sigma_f2 0.26 0.27 0.038 0.044 0.21 0.33 1.7 7.0 22.
3 sigma_g3 0.19 0.19 0.043 0.041 0.12 0.26 1.3 11. 35.
4 sigma_f4 0.084 0.062 0.060 0.047 0.023 0.21 1.7 6.6 31.
5 lengthscale_f1 0.22 0.22 0.031 0.031 0.15 0.26 1.8 6.3 18.
6 lengthscale_f2 0.24 0.23 0.031 0.037 0.20 0.29 2.2 5.6 22.
7 lengthscale_g3 0.66 0.65 0.16 0.16 0.42 0.93 1.3 11. 76.
8 sigma 0.23 0.23 0.0019 0.0022 0.23 0.24 1.0 265. 200.
Compare the model to the data
draws8 <- as_draws_matrix(draws8rhs)
Ef <- exp(apply(subset(draws8, variable='f'), 2, median))
Ef1 <- apply(subset(draws8, variable='f1'), 2, median)
Ef1 <- exp(Ef1 - mean(Ef1) + mean(log(data$births_relative100)))
Ef2 <- apply(subset(draws8, variable='f2'), 2, median)
Ef2 <- exp(Ef2 - mean(Ef2) + mean(log(data$births_relative100)))
Ef_day_of_week <- apply(subset(draws8, variable='f_day_of_week'), 2, median)
Ef_day_of_week <- exp(Ef_day_of_week - mean(Ef_day_of_week) + mean(log(data$births_relative100)))
Ef3 <- apply(subset(draws8, variable='f3'), 2, median)
Ef3 <- exp(Ef3 - mean(Ef3) + mean(log(data$births_relative100)))
Ef4 <- apply(subset(draws8, variable='beta_f4'), 2, median)*sd(log(data$births_relative100))
Ef4 <- exp(Ef4)*100
Efloats <- apply(subset(draws8, variable='beta_f5'), 2, median)*sd(log(data$births_relative100))
Efloats <- exp(Efloats)*100
floats1988<-c(memorial_days[20], labor_days[c(20,40)], thanksgiving_days[c(20,40)])-6939
Ef4float <- Ef4
Ef4float[floats1988] <- Ef4float[floats1988]*Efloats[c(1,2,2,3,3)]/100
pf <- data %>%
mutate(Ef = Ef) %>%
ggplot(aes(x=date, y=births_relative100)) + geom_point(color=set1[2], alpha=0.2) +
geom_point(aes(y=Ef), color=set1[1], alpha=0.2) +
labs(x="Date", y="Relative number of births")
pf1 <- data %>%
mutate(Ef1 = Ef1) %>%
ggplot(aes(x=date, y=births_relative100)) + geom_point(color=set1[2], alpha=0.2) +
geom_line(aes(y=Ef1), color=set1[1]) +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births")
pf2 <- data %>%
mutate(Ef2 = Ef2) %>%
group_by(day_of_year2) %>%
summarise(meanbirths=mean(births_relative100), meanEf2=mean(Ef2)) %>%
ggplot(aes(x=as.Date("1987-12-31")+day_of_year2, y=meanbirths)) + geom_point(color=set1[2], alpha=0.2) +
scale_x_date(date_breaks = "1 month", date_labels = "%b") +
geom_line(aes(y=meanEf2), color=set1[1]) +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births of year")
pf3 <- ggplot(data=data, aes(x=day_of_week, y=births_relative100)) + geom_point(color=set1[2], alpha=0.2) +
scale_x_continuous(breaks = 1:7, labels=c('Mon','Tue','Wed','Thu','Fri','Sat','Sun')) +
geom_line(data=data.frame(x=1:7,y=Ef_day_of_week), aes(x=x, y=Ef_day_of_week), color=set1[1]) +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births of week")
N=length(data$id)
pf3b <- data %>%
mutate(Ef3 = Ef3*Ef1/100) %>%
ggplot(aes(x=date, y=births_relative100)) + geom_point(color=set1[2], alpha=0.2) +
geom_point(aes(y=Ef3), color=set1[1], size=0.1) +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births") +
annotate("text",x=as.Date("1989-08-01"),y=(Ef3*Ef1/100)[c((N-5):(N-4), N, N-6)],label=c("Mon","Tue","Sat","Sun"))
f13 <- data %>% filter(year==1988)%>%select(day,date)%>%mutate(y=Ef4float)%>%filter(day==13)
pf2b <-data.frame(x=as.Date("1988-01-01")+0:365, y=Ef4float) %>%
ggplot(aes(x=x,y=y)) + geom_line(color=set1[1]) +
scale_x_date(date_breaks = "1 month", date_labels = "%b") +
geom_hline(yintercept=100, color='gray') +
labs(x="Date", y="Relative number of births of year") +
annotate("text",x=as.Date("1988-01-01"),y=Ef4float[1]-1,label="New year") +
annotate("text",x=as.Date("1988-02-14"),y=Ef4float[45]+1.5,label="Valentine's day") +
annotate("text",x=as.Date("1988-02-29"),y=Ef4float[60]-2.5,label="Leap day") +
annotate("text",x=as.Date("1988-04-01"),y=Ef4float[92]-1.5,label="April 1st") +
annotate("text",x=as.Date("1988-07-04"),y=Ef4float[186]-1.5,label="Independence day") +
annotate("text",x=as.Date("1988-10-31"),y=Ef4float[305]-1.5,label="Halloween") +
annotate("text",x=as.Date("1988-12-24"),y=Ef4float[360]-2,label="Christmas") +
annotate("text",x=as.Date("1988-05-30"),y=Ef4float[151]-2,label="Memorial day") +
annotate("text",x=as.Date("1988-09-05"),y=Ef4float[249]-1.5,label="Labor day") +
annotate("text",x=as.Date("1988-11-24"),y=Ef4float[329]-1,label="Thanksgiving")+
geom_point(data=f13,aes(x=date,y=y), size=3, shape=1)
(pf + pf1) / (pf2 + pf3b) / (pf2b)
Visually we get quite similar result as with \(t_\nu\) prior. When we compare the models with LOO-CV, there is not much difference between these priors.
loo8rhs<-fit8rhs$loo()
loo_compare(list(`Model 8 Students t`=loo8tnu,`Model 8 RHS`=loo8rhs))
elpd_diff se_diff
Model 8 Students t 0.0 0.0
Model 8 RHS -8.5 5.1
Further improvements for the day of year effect
It’s unlikely that day of year effect would be unstructured with some distribution like RHS, and thus instead of trying to find a prior distribution that would improve LOO-CV, it would make more sense to further add structural information. For example, it would be possible to add more known special days and take into account that a special day effect and weekend effect probably are not additive. Furthermore if there are less births during some day, the births need to happen some other day and it can be assumed that there would be corresponding excess of births before of after a bank holiday. This ringing around days with less births is not simple as it is also affected whether the previous and following days are weekend days. This all gets more complicated than we want to include in this case study, but the reader can see how the similar gradual model building could be made by adding additional components. Eventually it is likely that there starts to be worry of overfitting, but integration over the unknown alleviates that and looking at the predictive performance estimates such LOO-CV can help to decide when the additional model components don’t improve the predictive performance or can’t be well identified.
Residual analysis
We can get further ideas for how to improve the model also by looking at the residuals.
draws8 <- as_draws_matrix(draws8tnu)
Ef <- exp(apply(subset(draws8, variable='f'), 2, median))
data %>%
mutate(Ef = Ef) %>%
ggplot(aes(x=date, y=log(births_relative100/Ef))) + geom_point(color=set1[2]) +
geom_hline(yintercept=0, color='gray') +
scale_x_date(date_breaks = "1 year", date_labels = "%Y") +
theme(panel.grid.major.x=element_line(color='gray',size=1))
We can see some structure, specifically in years 1969–1978 the residual has negative peak in the middle of the year, while in years 1981–1988 the residual has positive peak in the middle of the year. This kind of pattern appears as we use the same seasonal effect for all years, but the magnitude of seasonal effect is changing. It would be possible to modify the model to include gradually changing seasonal effect, but leave it out from this case study.
The best model so far explains already 94% of the variance (LOO-R2).
draws8 <- as_draws_matrix(draws8tnu)
f <- exp(subset(draws8, variable='f'))
loo8tnu <- fit8tnu$loo(save_psis=TRUE)
Efloo <- E_loo(f, psis_object=loo8tnu$psis_object)$value
LOOR2 <- 1-var(log(data$births_relative100/Efloo))/var(log(data$births_relative100))
print(LOOR2, digits=2)
[1] 0.94
As it seems we could still improve by adding more structure and time varying seasonal effect, it seems the variability in the number of births from day to day is quite well predictable. Of course big part of the variation is due to planned induced births and c-sections, and thus hospitals do already control the number of births per day and there is no really practical use for the result. However there are plenty of similar time series, for example, in consumer behavior that are affected by special days.
More accurate inference
During all the iterative model building we favored optimization and short MCMC chains. In the end we also run with higher adapt_delta to reduce the probability of divergences, higher maximum treedepth to ensure higher effective sample size per iteration (ESS per second doesn’t necessarily improve), and run much longer chains, but didn’t see practical differences in plots or LOO-CV values. As running these longer chains can take hours they are not run as part of this notebook. An example of how to reduce probability of divergences and increase maximum treedepth is shown below (there is rarely need to increase adapt_delta larger than 0.95 and if there are still divergences with adapt_delta equal to 0.99, the posterior has serious problems and it should be considered whether re-parameterization, better data or more informative priors could help).
## fit8tnu <- model8tnu$sample(data=standata8, chains=4, parallel_chains=4,
## adapt_delta=0.95, max_treedepth=15)
LS0tCnRpdGxlOiAiQmF5ZXNpYW4gd29ya2Zsb3cgYm9vayAtIEJpcnRoZGF5cyIKYXV0aG9yOiAiR2VsbWFuLCBWZWh0YXJpLCBTaW1wc29uLCBldCBhbCIKZGF0ZTogIkZpcnN0IHZlcnNpb24gMjAyMC0xMi0yOC4gTGFzdCBtb2RpZmllZCBgciBmb3JtYXQoU3lzLkRhdGUoKSlgLiIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICB0aGVtZTogcmVhZGFibGUKICAgIHRvYzogdHJ1ZQogICAgdG9jX2RlcHRoOiAzCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKLS0tCldvcmtmbG93IGZvciBpdGVyYXRpdmUgYnVpbGRpbmcgb2YgYSB0aW1lIHNlcmllcyBtb2RlbC4KCldlIGFuYWx5c2UgdGhlIHJlbGF0aXZlIG51bWJlciBvZiBiaXJ0aHMgcGVyIGRheSBpbiBVU0EgMTk2OS0xOTg4CnVzaW5nIEdhdXNzaWFuIHByb2Nlc3MgdGltZSBzZXJpZXMgbW9kZWwgd2l0aCBzZXZlcmFsIG1vZGVsCmNvbXBvbmVudHMgdGhhdCBjYW4gZXhwbGFpbiB0aGUgbG9uZyB0ZXJtLCBzZWFzb25hbCwgd2Vla2x5LCBkYXkgb2YKeWVhciwgYW5kIHNwZWNpYWwgZmxvYXRpbmQgZGF5IHZhcmlhdGlvbi4KClN0YW4gbW9kZWwgY29kZXMgYXJlIGF2YWlsYWJsZSBpbiBbdGhlIGNvcnJlc3BvbmRpbmcgZ2l0IHJlcG9dKGh0dHBzOi8vZ2l0aHViLmNvbS9hdmVodGFyaS9jYXNlc3R1ZGllcy90cmVlL21hc3Rlci9CaXJ0aGRheXMpCgotLS0tLS0tLS0tLS0tCgoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChtZXNzYWdlPUZBTFNFLCBlcnJvcj1GQUxTRSwgd2FybmluZz1GQUxTRSwgY29tbWVudD1OQSwgY2FjaGU9RkFMU0UpCiMgc3dpdGNoIHRoaXMgdG8gVFJVRSB0byBzYXZlIGZpZ3VyZXMgaW4gc2VwYXJhdGUgZmlsZXMKc2F2ZWZpZ3MgPC0gRkFMU0UKYGBgCgojIyMjIExvYWQgcGFja2FnZXMKCmBgYHtyIH0KbGlicmFyeSgicnByb2pyb290IikKcm9vdDwtaGFzX2ZpbGUoIi5Xb3JrZmxvdy1FeGFtcGxlcy1yb290IikkbWFrZV9maXhfZmlsZSgpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGNtZHN0YW5yKQpsaWJyYXJ5KHBvc3RlcmlvcikKb3B0aW9ucyhwaWxsYXIubmVnID0gRkFMU0UsIHBpbGxhci5zdWJ0bGU9RkFMU0UsIHBpbGxhci5zaWdmaWc9MikKbGlicmFyeShsb28pCmxpYnJhcnkoYmF5ZXNwbG90KQp0aGVtZV9zZXQoYmF5ZXNwbG90Ojp0aGVtZV9kZWZhdWx0KGJhc2VfZmFtaWx5ID0gInNhbnMiKSkKbGlicmFyeShwYXRjaHdvcmspCnNldDEgPC0gUkNvbG9yQnJld2VyOjpicmV3ZXIucGFsKDcsICJTZXQxIikKYGBgCgpVc2UgRW5nbGlzaCBmb3IgbmFtZXMgb2Ygd2Vla2RheXMgYW5kIG1vbnRocwoKYGBge3IgfQpTeXMuc2V0bG9jYWxlKCJMQ19USU1FIiwgImVuX0dCLlVURi04IikKYGBgCgojIyBMb2FkIGFuZCBwbG90IGRhdGEKCkxvYWQgYmlydGhkYXlzIHBlciBkYXkgaW4gVVNBIDE5NjktMTk4ODoKCmBgYHtyIH0KZGF0YSA8LSByZWFkX2Nzdihyb290KCJCaXJ0aGRheXMvZGF0YSIsICJiaXJ0aHNfdXNhXzE5NjkuY3N2IikpCmBgYAoKQWRkIGRhdGUgdHlwZSBjb2x1bW4gZm9yIHBsb3R0aW5nCgpgYGB7ciB9CmRhdGEgPC0gZGF0YSAlPiUKICBtdXRhdGUoZGF0ZSA9IGFzLkRhdGUoIjE5NjgtMTItMzEiKSArIGlkLAogICAgICAgICBiaXJ0aHNfcmVsYXRpdmUxMDAgPSBiaXJ0aHMvbWVhbihiaXJ0aHMpKjEwMCkKYGBgCgojIyMgUGxvdCBhbGwgYmlydGhzCgpXZSBjYW4gc2VlIHNsb3cgdmFyaWF0aW9uIGluIHRyZW5kLCB5ZWFybHkgcGF0dGVybiwgYW5kIGVzcGVjaWFsbHkKaW4gdGhlIGxhdGVyIHllYXJzIHNwcmVhZCB0byBsb3dlciBhbmQgaGlnaGVyIHZhbHVlcy4KCmBgYHtyIH0KZGF0YSAlPiUKICBnZ3Bsb3QoYWVzKHg9ZGF0ZSwgeT1iaXJ0aHMpKSArIGdlb21fcG9pbnQoY29sb3I9c2V0MVsyXSkgKwogIGxhYnMoeD0iRGF0ZSIsIHk9IlJlbGF0aXZlIG51bWJlciBvZiBiaXJ0aHMiKQpgYGAKCiMjIyBQbG90IGFsbCBiaXJ0aHMgYXMgcmVsYXRpdmUgdG8gbWVhbgoKVG8gbWFrZSB0aGUgaW50ZXJwcmV0YXRpb24gd2Ugc3dpdGNoIHRvIGV4YW1pbmUgdGhlIHJlbGF0aXZlCmNoYW5nZSwgd2l0aCB0aGUgbWVhbiBsZXZlbCBkZW5vdGVkIHdpdGggMTAwLgoKYGBge3IgfQpkYXRhICU+JQogIGdncGxvdChhZXMoeD1kYXRlLCB5PWJpcnRoc19yZWxhdGl2ZTEwMCkpICsgZ2VvbV9wb2ludChjb2xvcj1zZXQxWzJdKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PTEwMCwgY29sb3I9J2dyYXknKSArCiAgbGFicyh4PSJEYXRlIiwgeT0iUmVsYXRpdmUgYmlydGhzIHBlciBkYXkiKQpgYGAKCiMjIyBQbG90IG1lYW4gcGVyIGRheSBvZiB5ZWFyCgpXZSBjYW4gc2VlIHRoZSBnZW5lcmljIHBhdHRlcm4gaW4geWVhcmx5IHNlYXNvbmFsIHRyZW5kIHNpbXBseSBieQphdmVyYWdpbmcgb3ZlciBlYWNoIGRheSBvZiB5ZWFyIChkYXlfb2ZfeWVhciBoYXMgbnVtYmVycyBmcm9tIDEgdG8KMzY2IGV2ZXJ5IHllYXIgd2l0aCBsZWFwIGRheSBiZWluZyA2MCBhbmQgMXN0IE1hcmNoIDYxIGFsc28gb24Kbm9uLWxlYXAteWVhcnMpLgoKYGBge3IgfQpkYXRhICU+JQogIGdyb3VwX2J5KGRheV9vZl95ZWFyMikgJT4lCiAgc3VtbWFyaXNlKG1lYW5iaXJ0aHM9bWVhbihiaXJ0aHNfcmVsYXRpdmUxMDApKSAlPiUKICBnZ3Bsb3QoYWVzKHg9YXMuRGF0ZSgiMTk4Ni0xMi0zMSIpK2RheV9vZl95ZWFyMiwgeT1tZWFuYmlydGhzKSkgKwogIGdlb21fcG9pbnQoY29sb3I9c2V0MVsyXSkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdD0xMDAsIGNvbG9yPSdncmF5JykgKwogIHNjYWxlX3hfZGF0ZShkYXRlX2JyZWFrcyA9ICIxIG1vbnRoIiwgZGF0ZV9sYWJlbHMgPSAiJWIiKSArCiAgbGFicyh4PSJEYXkgb2YgeWVhciIsIHk9IlJlbGF0aXZlIGJpcnRocyBwZXIgZGF5IG9mIHllYXIiKQpgYGAKCiMjIyBQbG90IG1lYW4gcGVyIGRheSBvZiB3ZWVrCgpXZSBjYW4gc2VlIHRoZSBnZW5lcmljIHBhdHRlcm4gaW4gd2Vla2x5IHRyZW5kIHNpbXBseSBieSBhdmVyYWdpbmcKb3ZlciBlYWNoIGRheSBvZiB3ZWVrLgoKYGBge3IgfQpkYXRhICU+JQogIGdyb3VwX2J5KGRheV9vZl93ZWVrKSAlPiUKICBzdW1tYXJpc2UobWVhbmJpcnRocz1tZWFuKGJpcnRoc19yZWxhdGl2ZTEwMCkpICU+JQogIGdncGxvdChhZXMoeD1kYXlfb2Zfd2VlaywgeT1tZWFuYmlydGhzKSkgKwogIGdlb21fcG9pbnQoY29sb3I9c2V0MVsyXSwgc2l6ZT00KSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PTEwMCwgY29sb3I9J2dyYXknKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IDE6NywgbGFiZWxzPWMoJ01vbicsJ1R1ZScsJ1dlZCcsJ1RodScsJ0ZyaScsJ1NhdCcsJ1N1bicpKSArCiAgbGFicyh4PSJEYXkgb2Ygd2VlayIsIHk9IlJlbGF0aXZlIG51bWJlciBvZiBiaXJ0aHMgb2Ygd2VlayIpCmBgYAoKIyMgUHJldmlvdXMgYW5hbHlzZXMKCldlIGhhdmUgYW5hbHlzZWQgdGhlIHNhbWUgZGF0YSBiZWZvcmUgaW4gQkRBMyBhbmQgdGh1cyBoYWQgaWRlYSBvZgp3aGF0IGtpbmQgb2YgbW9kZWwgdG8gdXNlLiBGb3IgQkRBMyB3ZSB1c2VkIEdQc3R1ZmYgc29mdHdhcmUgd2hpY2gKaXMgR2F1c3NpYW4gcHJvY2VzcyBzcGVjaWZpYyBzb2Z0d2FyZSBmb3IgTWF0bGFiIGFuZCBPY3RhdmUuIEFzClN0YW4gaGFzIGFpbWVkIHRvIGJlIHZlcnkgZ2VuZXJpYyBpdCBjYW4gYmUgc2xvd2VyIHRoYW4gc3BlY2lhbGl6ZWQKc29mdHdhcmUgZm9yIHNvbWUgc3BlY2lmaWMgbW9kZWxzIHN1Y2ggYXMgR2F1c3NpYW4gcHJvY2Vzc2VzLCBidXQKU3RhbiBwcm92aWRlcyBtb3JlIGZsZXhpYmlsaXR5IGluIHRoZSBtb2RlbCBkZWZpbml0aW9uLgoKUml1dG9ydC1NYXlvbCBldCBhbCAoMjAyMCkgZGVtb25zdHJhdGUgSGlsYmVydCBzcGFjZSBhcHByb3hpbWF0ZQpiYXNpcyBmdW5jdGlvbiBhcHByb3hpbWF0aW9uIG9mIEdhdXNzaWFuIHByb2Nlc3NlcyBhbHNvIGZvciB0aGUKc2FtZSBiaXJ0aGRheSBkYXRhLiBJbiB0aGUgZXhwZXJpbWVudHMgdGhlIGluZmVyZW5jZSB3YXMgc2xvd2VyCnRoYW4gZXhwZWN0ZWQgcmFpc2luZyBzdXNwaWNpb24gb2YgaW5lZmZpY2llbnQgbW9kZWwgY29kZSBvciBiYWQKcG9zdGVyaW9yIHNoYXBlIGR1ZSB0byBiYWQgbW9kZWwgc3BlY2lmaWNhdGlvbi4KCiMjIFdvcmtmbG93IGZvciBxdWljayBpdGVyYXRpdmUgbW9kZWwgYnVpbGRpbmcKCkV2ZW4gd2UgaGF2ZSBnZW5lcmFsIGlkZWEgZm9yIHRoZSBtb2RlbCAoc2xvdyB0cmVuZCwgc2Vhc29uYWwKdHJlbmQsIHdlZWtkYXkgZWZmZWN0LCBldGMpLCBhZGRpbmcgdGhlbSBhbGwgYXQgb25jZSB0byB0aGUgbW9kZWwKbWFrZXMgdGhlIG1vZGVsIGNvbXBsZXggYW5kIGRpZmZpY3VsdCB0byBkZWJ1ZyBhbmQgc29sdmUgdGhlCmNvbXB1dGF0aW9uYWwgcHJvYmxlbXMuIEl0IGlzIHRodXMgbmF0dXJhbCB0byBidWlsZCB0aGUgbW9kZWwKZ3JhZHVhbGx5IGFuZCBjaGVjayB0aGF0IGVhY2ggYWRkaXRpb24gd29ya3MgYmVmb3JlIGFkZGluZyB0aGUgbmV4dAptb2RlbCBjb21wb25lbnQuIER1cmluZyB0aGlzIGl0ZXJhdGl2ZSBtb2RlbCBidWlsZGluZyB3ZSB3YW50IHRoZQppbmZlcmVuY2UgdG8gYmUgZmFzdCwgYnV0IGl0IGRvZXNuJ3QgbmVlZCB0byBiZSB2ZXJ5IGFjY3VyYXRlIGFzCmxvbmcgYXMgcXVhbGl0YXRpdmVseSB0aGUgbmV3IG1vZGVsIGlzIHJlYXNvbmFibGUuIEZvciBxdWljawp0ZXN0aW5nIGFuZCBpdGVyYXRpdmUgbW9kZWwgYnVpbGRpbmcgd2UgY2FuIHVzZSBvcHRpbWl6YXRpb24gYW5kCnNob3J0ZXIgTUNNQyBjaGFpbnMgdGhhdCB3ZSB3b3VsZCBub3QgcmVjb21tZW5kIGZvciB0aGUgZmluYWwKaW5mZXJlbmNlLiAgRnVydGhlcm1vcmUsIGluIHRoaXMgc3BlY2lmaWMgZXhhbXBsZSwgdGhlIG5ldwphZGRpdGlvbnMgYXJlIHF1YWxpdGF0aXZlbHkgc28gY2xlYXIgaW1wcm92ZW1lbnRzIHRoYXQgdGhlcmUgaXMgbm8KbmVlZCBmb3IgcXVhbnRpdGF0aXZlIG1vZGVsIGNvbXBhcmlzb24gd2hldGhlciB0aGUgYWRkaXRpb25zIGFyZQpgYHNpZ25pZmljYW50JycgKHNlZSBhbHNvIE5hdmFycm8sIDIwMTkpIGFuZCB0aGVyZSBpcyBubyBkYW5nZXIgb2YKb3ZlcmZpdHRpbmcuIEFsdGhvdWdoIHRoZXJlIGlzIG9uZSBwYXJ0IG9mIHRoZSBtb2RlbCB3aGVyZSB0aGUgZGF0YQppcyB3ZWFrbHkgaW5mb3JtYXRpdmUgYW5kIHRoZSBwcmlvciBjaG9pY2VzIHNlZW0gdG8gbWF0dGVyIGFuZAp3ZSdsbCBnZXQgYmFjayB0byB0aGlzIGFuZCBjb25zZXF1ZW5jZXMgbGF0ZXIuIE92ZXJhbGwgd2UgYnVpbGQKdGVucyBvZiBkaWZmZXJlbnQgbW9kZWxzLCBidXQgaWxsdXN0cmF0ZSBoZXJlIG9ubHkgdGhlIG1haW4gbGluZS4KCiMjIE1vZGVscyBmb3IgcmVsYXRpdmUgbnVtYmVyIG9mIGJpcnRoZGF5cwoKQXMgdGhlIHJlbGF0aXZlIG51bWJlciBvZiBiaXJ0aHMgaXMgcG9zaXRpdmUgaXQncyBuYXR1cmFsIHRvIG1vZGVsCnRoZSBsb2dhcml0aG0gdmFsdWUuIFRoZSBnZW5lcmljIGZvcm0gb2YgdGhlIG1vZGVscyBpcwokJAp5IFxzaW0gXG1ib3h7bm9ybWFsfShmKHgpLCBcc2lnbWEpLAokJAp3aGVyZSAkZiQgaXMgZGlmZmVyZW50IGFuZCBncmFkdWFsbHkgbW9yZSBjb21wbGV4IGZ1bmN0aW9uCmNvbmRpdGlvbmFsIG9uICR4JCB0aGF0IGluY2x1ZGVzIHJ1bm5pbmcgZGF5IG51bWJlciwgZGF5IG9mIHllYXIsCmRheSBvZiB3ZWVrIGFuZCBldmVudHVhbGx5IHNvbWUgc3BlY2lhbCBmbG9hdGluZyBVUyBiYW5rIGhvbGlkYXlzLgoKIyMjIE1vZGVsIDE6IFNsb3cgdHJlbmQKClRoZSBtb2RlbCAxIGlzIGp1c3QgdGhlIHNsb3cgdHJlbmQgb3ZlciB0aGUgeWVhcnMgdXNpbmcgSGlsYmVydApzcGFjZSBiYXNpcyBmdW5jdGlvbiBhcHByb3hpbWF0ZWQgR2F1c3NpYW4gcHJvY2VzcwokJApmID0gXG1ib3h7aW50ZXJjZXB0fSArIGZfMVxcClxtYm94e2ludGVyY2VwdH0gXHNpbSBcbWJveHtub3JtYWx9KDAsMSlcXApmXzEgXHNpbSBcbWJveHtHUH0oMCxLXzEpCiQkCndoZXJlIEdQIGhhcyBleHBvbmVudGlhdGVkIHF1YWRyYXRpYyBjb3ZhcmlhbmNlIGZ1bmN0aW9uLgoKSW4gdGhpcyBwaGFzZSB0aGUgY29kZSBmcm9tIFJpdXRvcnQtTWF5b2wgZXQgYWwuKDIwMjApIHdhcyBjbGVhbmVkCmFuZCB3cml0dGVuIHRvIGJlIG1vcmUgZWZmaWNpZW50LCBidXQgb25seSB0aGUgb25lIEdQIGNvbXBvbmVudCB3YXMKaW5jbHVkZWQgdG8gbWFrZSB0aGUgdGVzdGluZyBlYXNpZXIuIEFsdGhvdWdoIHRoZSBjb2RlIHdhcyBtYWRlCm1vcmUgZWZmaWNpZW50LCB0aGUgYWltIHdhc24ndCB0byBtYWtlIGl0IHRoZSBmYXN0ZXN0IHBvc3NpYmxlIGFzCnRoZSBsYXRlciBtb2RlbCBjaGFuZ2VzIG1heSBoYXZlIGJpZ2dlciBlZmZlY3Qgb24gdGhlIHBlcmZvcm1hbmNlCihpdCdzIGdvb2QgbyBhdm9pZCBwcmVtYXR1cmUgb3B0aW1pemF0aW9uKS4gV2UgYWxzbyB1c2UgcXVpdGUgc21hbGwKbnVtYmVyIG9mIGJhc2lzIGZ1bmN0aW9ucyB0byBtYWtlIHRoZSBjb2RlIHJ1biBmYXN0ZXIsIGFuZCBvbmx5CmxhdGVyIGV4YW1pbmUgbW9yZSBjYXJlZnVsbHkgd2hldGhlciB0aGUgbnVtYmVyIG9mIGJhc2lzIGZ1bmN0aW9uCmlzIHN1ZmZpY2llbnQgY29tcGFyZWQgdG8gdGhlIHBvc3RlcmlvciBvZiB0aGUgbGVuZ3RoIHNjYWxlIChzZWUsClJpdXRvcnQtTWF5b2wgZXQgYWwuLCAyMDIwKS4KCkNvbXBpbGUgU3RhbiBtb2RlbCBbZ3BiZjEuc3Rhbl0oaHR0cHM6Ly9naXRodWIuY29tL2F2ZWh0YXJpL2Nhc2VzdHVkaWVzL2Jsb2IvbWFzdGVyL0JpcnRoZGF5cy9ncGJmMS5zdGFuKSB3aGljaCBpbmNsdWRlcyBbZ3BiYXNpc2Z1bl9mdW5jdGlvbnMxLnN0YW5dKGh0dHBzOi8vZ2l0aHViLmNvbS9hdmVodGFyaS9jYXNlc3R1ZGllcy9ibG9iL21hc3Rlci9CaXJ0aGRheXMvZ3BiYXNpc2Z1bl9mdW5jdGlvbnMxLnN0YW4pCgpgYGB7ciBtb2RlbDEsIHJlc3VsdHM9J2hpZGUnfQptb2RlbDEgPC0gY21kc3Rhbl9tb2RlbChzdGFuX2ZpbGUgPSByb290KCJCaXJ0aGRheXMiLCAiZ3BiZjEuc3RhbiIpLAogICAgICAgICAgICAgICAgICAgICAgICBpbmNsdWRlX3BhdGhzID0gcm9vdCgiQmlydGhkYXlzIikpCmBgYAoKRGF0YSB0byBiZSBwYXNzZWQgdG8gU3RhbgoKYGBge3IgfQpzdGFuZGF0YTEgPC0gbGlzdCh4PWRhdGEkaWQsCiAgICAgICAgICAgICAgICAgIHk9bG9nKGRhdGEkYmlydGhzX3JlbGF0aXZlMTAwKSwKICAgICAgICAgICAgICAgICAgTj1sZW5ndGgoZGF0YSRpZCksCiAgICAgICAgICAgICAgICAgIGNfZjE9MS41LCAjIGZhY3RvciBjIG9mIGJhc2lzIGZ1bmN0aW9ucyBmb3IgR1AgZm9yIGYxCiAgICAgICAgICAgICAgICAgIE1fZjE9MjApICAjIG51bWJlciBvZiBiYXNpcyBmdW5jdGlvbnMgZm9yIEdQIGZvciBmMQpgYGAKCkFzIHRoZSBiYXNpcyBmdW5jdGlvbiBhcHByb3hpbWF0aW9uIGFuZCBwcmlvcnMgcmVzdHJpY3QgdGhlCmNvbXBsZXhpdHkgb2YgR1AsIHdlIGNhbiBzYWZlbHkgdXNlIG9wdGltaXphdGlvbiB0byBnZXQgYSB2ZXJ5CnF1aWNrIGluaXRpYWwgcmVzdWx0IHRvIGNoZWNrIHRoYXQgdGhlIG1vZGVsIGNvZGUgaXMgY29tcHV0aW5nIHdoYXQKd2UgaW50ZW5kZWQuIEFzIHRoZXJlIGFyZSBvbmx5IDE0IHBhcmFtZXRlcnMgYW5kIDczMDUgb2JzZXJ2YXRpb25zCml0J3MgbGlrZWx5IHRoYXQgdGhlIHBvc3RlcmlvciBpcyBjbG9zZSB0byBub3JtYWwgKGluIHVuY29uc3RyYWluZWQKc3BhY2UpLiBJbiB0aGlzIGNhc2UgdGhlIG9wdGltaXphdGlvbiB0YWtlcyBsZXNzIHRoYW4gb25lIHNlY29uZAp3aGlsZSBNQ01DIHNhbXBsaW5nIHdpdGggZGVmYXVsdCBvcHRpb25zIHdvdWxkIGhhdmUgdGFrZW4gc2V2ZXJhbAptaW51dGVzLiBBbHRob3VnaCB0aGlzIHJlc3VsdCBjYW4gYmUgdXNlZnVsIGluIGEgcXVpY2sgd29ya2Zsb3csCnRoZSByZXN1bHQgc2hvdWxkIG5vdCBiZSB1c2VkIGFzIHRoZSBmaW5hbCByZXN1bHQuCgpgYGB7ciBvcHQxLCByZXN1bHRzPSdoaWRlJ30Kb3B0MSA8LSBtb2RlbDEkb3B0aW1pemUoZGF0YSA9IHN0YW5kYXRhMSwgaW5pdD0wLCBhbGdvcml0aG09J2JmZ3MnKQpgYGAKCkNoZWNrIHdoZXRoZXIgcGFyYW1ldGVycyBoYXZlIHJlYXNvbmFibGUgdmFsdWVzCgpgYGB7ciB9Cm9kcmF3czEgPC0gb3B0MSRkcmF3cygpCnN1YnNldChvZHJhd3MxLCB2YXJpYWJsZT1jKCdpbnRlcmNlcHQnLCdzaWdtYV9mMScsJ2xlbmd0aHNjYWxlX2YxJywnc2lnbWEnKSkKYGBgCgpDb21wYXJlIHRoZSBtb2RlbCB0byB0aGUgZGF0YQoKYGBge3IgfQpvRWYgPC0gZXhwKGFzLm51bWVyaWMoc3Vic2V0KG9kcmF3czEsIHZhcmlhYmxlPSdmJykpKQpkYXRhICU+JQogIG11dGF0ZShvRWYgPSBvRWYpICU+JQogIGdncGxvdChhZXMoeD1kYXRlLCB5PWJpcnRoc19yZWxhdGl2ZTEwMCkpICsKICBnZW9tX3BvaW50KGNvbG9yPXNldDFbMl0sIGFscGhhPTAuMikgKwogIGdlb21fbGluZShhZXMoeT1vRWYpLCBjb2xvcj1zZXQxWzFdKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PTEwMCwgY29sb3I9J2dyYXknKSArCiAgbGFicyh4PSJEYXRlIiwgeT0iUmVsYXRpdmUgbnVtYmVyIG9mIGJpcnRocyIpCmBgYAoKQWZ0ZXIgd2UgZ2V0IHRoZSBtb2RlbCB3b3JraW5nIHVzaW5nIG9wdGltaXphdGlvbiB3ZSBjYW4gY29tcGFyZQp0aGUgcmVzdWx0IHRvIHVzaW5nIHNob3J0IE1DTUMgY2hhaW5zIHdoaWNoIHdpbGwgYWxzbyBwcm92aWRlIHVzCmFkZGl0aW9uYWwgaW5mb3JtYXRpb24gb24gc3BlZWQgb2YgZGlmZmVyZW50IGNvZGUgaW1wbGVtZW50YXRpb25zCmZvciB0aGUgc2FtZSBtb2RlbC4gV2UgaW50ZW50aW9uYWxseSB1c2UganVzdCAxLzEwdGggbGVuZ3RoIGZyb20KdGhlIHVzdWFsIHJlY29tbWVuZGF0aW9uLCBhcyBkdXJpbmcgdGhlIGl0ZXJhdGl2ZSBtb2RlbCBidWlsZGluZyBhCnJvdWdoIHJlc3VsdHMgYXJlIHN1ZmZpY2llbnQuIFdoZW4gdGVzdGluZyB0aGUgY29kZSB3ZSBpbml0aWFsbHkKdXNlZCBqdXN0IG9uZSBjaGFpbiwgYnV0IGF0IHRoaXMgcG9pbnQgcnVubmluZyBmb3VyIGNoYWlucyB3aXRoCmZvdXIgY29yZSBDUFUgZG9lc24ndCBhZGQgbXVjaCB0byB0aGUgd2FsbCBjbG9jayB0aW1lLCBidXQgZ2l2ZXMKbW9yZSBpbmZvcm1hdGlvbiBvZiBob3cgZWFzeSBpdCBpcyBzYW1wbGUgZnJvbSB0aGUgcG9zdGVyaW9yIGFuZApjYW4gcmV2ZWFsIGlmIHRoZXJlIGFyZSBtdWx0aXBsZSBtb2Rlcy4gQWx0aG91Z2ggdGhlIHJlc3VsdCBmcm9tCnNob3J0IGNoYWlucyBjYW4gYmUgdXNlZnVsIGluIGEgcXVpY2sgd29ya2Zsb3csIHRoZSByZXN1bHQgc2hvdWxkCm5vdCBiZSB1c2VkIGFzIHRoIGZpbmFsIHJlc3VsdC4KCmBgYHtyIGZpdDEsIHJlc3VsdHM9J2hpZGUnfQpmaXQxIDwtIG1vZGVsMSRzYW1wbGUoZGF0YT1zdGFuZGF0YTEsIGl0ZXJfd2FybXVwPTEwMCwgaXRlcl9zYW1wbGluZz0xMDAsCiAgICAgICAgICAgICAgICAgICAgICBjaGFpbnM9NCwgcGFyYWxsZWxfY2hhaW5zPTQsIHNlZWQ9Mzg5MSkKYGBgCgpEZXBlbmRpbmcgb24gdGhlIHJhbmRvbSBzZWVkIGFuZCBsdWNrLCB3ZSBzb21ldGltZXMgb2JzZXJ2ZWQgdGhhdApzb21lIG9mIHRoZSBjaGFpbnMgZ290IHN0dWNrIGluIGRpZmZlcmVudCBtb2Rlcy4gV2UgY291bGQgc2VlIHRoaXMKaW4gaGlnaCBSaGF0IGFuZCBsb3cgRVNTIGRpYWdub3N0aWMgdmFsdWVzLgoKYGBge3IgfQpkcmF3czEgPC0gZml0MSRkcmF3cygpCnN1bW1hcmlzZV9kcmF3cyhzdWJzZXQoZHJhd3MxLCB2YXJpYWJsZT1jKCdpbnRlcmNlcHQnLCdzaWdtYV9mMScsJ2xlbmd0aHNjYWxlX2YxJywnc2lnbWEnKSkpCmBgYAoKRXhhbWluaW5nIHRoZSB0cmFjZSBwbG90cyBzaG93cyB0aGUgbXVsdGltb2RhbGl0eSBjbGVhcmx5LgoKYGBge3IgfQptY21jX3RyYWNlKGRyYXdzMSwgcmVnZXhfcGFycz1jKCdpbnRlcmNlcHQnLCdzaWdtYV9mMScsJ2xlbmd0aHNjYWxlX2YxJywnc2lnbWEnKSkKYGBgCgpJbiB0aGlzIGNhc2UgaXQgd2FzIGVhc3kgdG8gZmlndXJlIG91dCB0aGF0IHNvbWUgb2YgdGhlIGNoYWlucyBnb3QKc3R1Y2sgaW4gcXVhbGl0YXRpdmVseSBtdWNoIHdvcnNlIG1vZGVzLiBXZSBkb24ndCBpbiBnZW5lcmFsCnJlY29tbWVuZCB0byBzdGFydCBmcm9tIHRoZSBtb2RlIGFzIHRoZSBtb2RlIGlzIG5vdCB1c3VhbGx5CnJlcHJlc2VudGF0aXZlIHBvaW50IGluIGhpZXJhcmNoaWNhbCBtb2RlbCBwb3N0ZXJpb3Igb3IgaW4gaGlnaApkaW1lbnNpb25hbCBwb3N0ZXJpb3IsIGJ1dCB3ZSBjYW4gdXNlIHRoaXMgYWdhaW4gdG8gc3BlZWQgdXAgdGhlCml0ZXJhdGl2ZSBtb2RlbCBidWlsZGluZyBhcyBsb25nIGFzIHdlIGNoZWNrIHRoYXQgdGhlIG9wdGltaXphdGlvbgpyZXN1bHQgaXMgc2Vuc2libGUgYW5kIGxhdGVyIGRvIG1vcmUgY2FyZWZ1bCBpbmZlcmVuY2UuIEFsdGhvdWdoCnRoZSByZXN1bHQgZnJvbSBzaG9ydCBjaGFpbnMgY2FuIGJlIHVzZWZ1bCBpbiBhIHF1aWNrIHdvcmtmbG93LCB0aGUKcmVzdWx0IHNob3VsZCBub3QgYmUgdXNlZCBhcyB0aGUgZmluYWwgcmVzdWx0LgoKYGBge3IgfQppbml0MSA8LSBzYXBwbHkoYygnaW50ZXJjZXB0Jywnc2lnbWFfZjEnLCdsZW5ndGhzY2FsZV9mMScsJ2JldGFfZjEnLCdzaWdtYScpLAogICAgICAgICAgICAgICAgZnVuY3Rpb24odmFyaWFibGUpIHthcy5udW1lcmljKHN1YnNldChvZHJhd3MxLCB2YXJpYWJsZT12YXJpYWJsZSkpfSkKYGBgCmBgYHtyIGZpdDFpbml0LCByZXN1bHRzPSdoaWRlJ30KZml0MSA8LSBtb2RlbDEkc2FtcGxlKGRhdGE9c3RhbmRhdGExLCBpdGVyX3dhcm11cD0xMDAsIGl0ZXJfc2FtcGxpbmc9MTAwLAogICAgICAgICAgICAgICAgICAgICAgY2hhaW5zPTQsIHBhcmFsbGVsX2NoYWlucz00LAogICAgICAgICAgICAgICAgICAgICAgaW5pdD1mdW5jdGlvbigpIHsgaW5pdDEgfSkKYGBgCgpXZSBub3cgb2JzZXJ2ZSBiZXR0ZXIgUmhhdCBhbmQgRVNTIGRpYWdub3N0aWMgdmFsdWVzLCBhbHRob3VnaCBkdWUKdG8gdmVyeSBzaG9ydCBjaGFpbnMgdGhleSBhcmUgbm90IHlldCBwZXJmZWN0LiBXZSBhcmUgbGlrZWx5IHRvCmFsc28gb2JzZXJ2ZSBIYW1pbHRvbmlhbiBNb250ZSBDYXJsbyBkaXZlcmdlbmNlcyBhbmQgdHJlZWRlcHRoCmV4Y2VlZGVuY2VzIGluIGR5bmFtaWMgYnVpbGRpbmcgb2YgdGhlIEhhbWlsdG9uaWFuIHRyYWplY3RvcnksCmJ1dCB0aGVyZSBpcyBubyBuZWVkIHRvIHdvcnJ5IGFib3V0IHRob3NlIGFzIGxvbmcgYXMgdGhlCm1vZGVsIHJlc3VsdHMgYXJlIHF1YWxpdGF0aXZlbHkgc2Vuc2libGUgYXMgdGhlc2UgY29tcHV0YXRpb25hbAppc3N1ZXMgY2FuIGFsc28gZ28gYXdheSB3aGVuIHRoZSBtb2RlbCBpdHNlbGYgaXMgaW1wcm92ZWQuIEluIGFsbAp0aGUgZm9sbG93aW5nIHNob3J0IE1DTUMgc2FtcGxpbmdzIHdlIGdldCBzb21lIG9yIG1hbnkgZGl2ZXJnZW5jZXMKYW5kIHVzdWFsbHkgdmVyeSBsYXJnZSBudW1iZXIgb2YgdHJlZWRlcHRoIGV4Y2VlZGVuY2VzLiBEaXZlcmdlbmNlcwppbmRpY2F0ZSBwb3NzaWJsZSBiaWFzIGFuZCBzaG91bGQgYmUgZXZlbnR1YWxseSBpbnZlc3RpZ2F0ZWQKY2FyZWZ1bGx5LiBUcmVlZGVwdGggZXhjZWVkZW5jZXMgaW5kaWNhdGUgc3Ryb25nIHBvc3RlcmlvcgpkZXBlbmRlbmNpZXMgYW5kIHNsb3cgbWl4aW5nIGFuZCBzb21ldGltZXMgdGhlIHBvc3RlcmlvciBjYW4gYmUKbXVjaCBpbXByb3ZlZCBieSBjaGFuZ2luZyB0aGUgcGFyYW1ldGVyaXphdGlvbiBvciBwcmlvcnMsIGJ1dCBhcwp0aGUgdHJlZWRlcHRoIGV4Y2VlZGVuY2VzIGRvbid0IGluZGljYXRlIGJpYXMgdGhlcmUgaXMgbm8gbmVlZCBmb3IKbW9yZSBjYXJlZnVsIGFuYWx5c2lzIGlmIHRoZSByZXN1bHRpbmcgRVNTIGFuZCBNQ1NFIHZhbHVlcyBhcmUgZ29vZApmb3IgdGhlIHB1cnBvc2UgaW4gaGFuZC4gIFdlJ2xsIGNvbWUgYmFjayBsYXRlciB0byBtb3JlIGNhcmVmdWwKYW5hbHlzaXMgb2YgdGhlIGZpbmFsIG1vZGVscy4KCmBgYHtyIH0KZHJhd3MxIDwtIGZpdDEkZHJhd3MoKQpzdW1tYXJpc2VfZHJhd3Moc3Vic2V0KGRyYXdzMSwgdmFyaWFibGU9YygnaW50ZXJjZXB0Jywnc2lnbWFfZjEnLCdsZW5ndGhzY2FsZV9mMScsJ3NpZ21hJykpKQpgYGAKClRyYWNlIHBsb3Qgc2hvd3Mgc2xvdyBtaXhpbmcgYnV0IG5vIG11bHRpbW9kYWxpdHkuCgpgYGB7ciB9Cm1jbWNfdHJhY2UoZHJhd3MxLCByZWdleF9wYXJzPWMoJ2ludGVyY2VwdCcsJ3NpZ21hX2YxJywnbGVuZ3Roc2NhbGVfZjEnLCdzaWdtYScpKQpgYGAKClRoZSBtb2RlbCByZXN1bHQgZnJvbSBzaG9ydCBNQ01DIGNoYWlucyBsb29rcyB2ZXJ5IHNpbWlsYXIgdG8gdGhlCm9wdGltaXphdGlvbiByZXN1bHQuCgpgYGB7ciB9CmRyYXdzMSA8LSBhc19kcmF3c19tYXRyaXgoZHJhd3MxKQpFZiA8LSBleHAoYXBwbHkoc3Vic2V0KGRyYXdzMSwgdmFyaWFibGU9J2YnKSwgMiwgbWVkaWFuKSkKZGF0YSAlPiUKICBtdXRhdGUoRWYgPSBFZikgJT4lCiAgZ2dwbG90KGFlcyh4PWRhdGUsIHk9YmlydGhzX3JlbGF0aXZlMTAwKSkgKyBnZW9tX3BvaW50KGNvbG9yPXNldDFbMl0sIGFscGhhPTAuMikgKwogIGdlb21fbGluZShhZXMoeT1FZiksIGNvbG9yPXNldDFbMV0pICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9MTAwLCBjb2xvcj0nZ3JheScpICsKICBsYWJzKHg9IkRhdGUiLCB5PSJSZWxhdGl2ZSBudW1iZXIgb2YgYmlydGhzIikKYGBgCgpJZiB3ZSBjb21wYXJlIHRoZSByZXN1bHQgZnJvbSBzaG9ydCBzYW1wbGluZyB0byBvcHRpbWl6aW5nLCB3ZQpkb24ndCBzZWUgcHJhY3RpY2FsIGRpZmZlcmVuY2UgaW4gdGhlIHByZWRpY3Rpb25zIChhbHRob3VnaCB3ZSBzZWUKbGF0ZXIgbW9yZSBkaWZmZXJlbmNlcyBiZXR3ZWVuIG9wdGltaXphdGlvbiBhbmQgTUNNQykuCgpgYGB7ciB9CmRhdGEgJT4lCiAgbXV0YXRlKEVmID0gRWYsCiAgICAgICAgIG9FZiA9IG9FZikgJT4lCiAgZ2dwbG90KGFlcyh4PUVmLCB5PW9FZikpICsgZ2VvbV9wb2ludChjb2xvcj1zZXQxWzJdKSArCiAgZ2VvbV9hYmxpbmUoKSArCiAgbGFicyh4PSJFZiBmcm9tIHNob3J0IE1hcmtvdiBjaGFpbiIsIHk9IkVmIGZyb20gb3B0aW1pemluZyIpCmBgYAoKQWZ0ZXIgdGhlIGZpcnN0IHZlcnNpb24gb2YgdGhpcyBub3RlYm9vaywgW05pa29sYXMgU2ljY2hhIGV4YW1pbmVkCm1vcmUgY2FyZWZ1bGx5IHRoZSBwb3N0ZXJpb3IKY29ycmVsYXRpb25zXShodHRwczovL2dpdGh1Yi5jb20vbnNpY2NoYS9iaXJ0aGRheSkgYW5kIG5vdGljZWQKc3Ryb25nIGNvcnJlbGF0aW9uIGJldHdlZW4gaW50ZXJjZXB0IGFuZCB0aGUgZmlyc3QgYmFzaXMKZnVuY3Rpb24uIFN0YW4ncyBkeW5hbWljIEhNQyBpcyBzbyBlZmZpY2llbnQgdGhhdCB0aGUgaW5mZXJlbmNlIGlzCnN1Y2Nlc2Z1bCBhbnl3YXkuIE5pa29sYXMgc3VnZ2VzdGVkIHJlbW92aW5nIHRoZSBpbnRlcmNlcHQKdGVybS4gVGhlIGludGVyY2VwdCB0ZXJtIGlzIG5vdCBuZWNlc3NhcmlseSBuZWVkZWQgYXMgdGhlIGRhdGEgaGFzCmJlZW4gY2VudGVyZWQuIFdlIHRlc3QgYSBtb2RlbCB3aXRob3V0IHRoZSBleHBsaWNpdCBpbnRlcmNlcHQgdGVybS4KCkNvbXBpbGUgU3RhbiBtb2RlbCBbZ3BiZjFiLnN0YW5dKGh0dHBzOi8vZ2l0aHViLmNvbS9hdmVodGFyaS9jYXNlc3R1ZGllcy9ibG9iL21hc3Rlci9CaXJ0aGRheXMvZ3BiZjFiLnN0YW4pCgpgYGB7ciBtb2RlbDFiLCByZXN1bHRzPSdoaWRlJ30KbW9kZWwxYiA8LSBjbWRzdGFuX21vZGVsKHN0YW5fZmlsZSA9IHJvb3QoIkJpcnRoZGF5cyIsICJncGJmMWIuc3RhbiIpLAogICAgICAgICAgICAgICAgICAgICAgICBpbmNsdWRlX3BhdGhzID0gcm9vdCgiQmlydGhkYXlzIikpCmBgYAoKV2Ugc2FtcGxlIHVzaW5nIHRoZSBkZWZhdWx0IGluaXRpYWxpemF0aW9uLgoKYGBge3IgZml0MWIsIHJlc3VsdHM9J2hpZGUnfQpmaXQxYiA8LSBtb2RlbDFiJHNhbXBsZShkYXRhPXN0YW5kYXRhMSwgaXRlcl93YXJtdXA9MTAwLCBpdGVyX3NhbXBsaW5nPTEwMCwKICAgICAgICAgICAgICAgICAgICAgIGNoYWlucz00LCBwYXJhbGxlbF9jaGFpbnM9NCwgc2VlZD0zODkxKQpgYGAKClRoZSBzYW1wbGluZyBwZXJmb3JtcyBiZXR0ZXIsIGluZGljYXRpbmcgdGhhdCB0aGUgc3Ryb25nIHBvc3Rlcmlvcgpjb3JyZWxhdGlvbiBpbiB0aGUgZmlyc3QgbW9kZWwgd2FzIGNhdXNpbmcgdHJvdWJsZXMgZm9yIHRoZQphZGFwdGF0aW9uIGluIHRoZSBzaG9ydCB3YXJtdXAgbGVhZGluZyBzb21lIGNoYWlucyB0byBzdGF5IHN0dWNrLgoKYGBge3IgfQpkcmF3czFiIDwtIGZpdDFiJGRyYXdzKCkKc3VtbWFyaXNlX2RyYXdzKHN1YnNldChkcmF3czFiLCB2YXJpYWJsZT1jKCdzaWdtYV9mMScsJ2xlbmd0aHNjYWxlX2YxJywnc2lnbWEnKSkpCmBgYAoKRXhhbWluaW5nIHRoZSB0cmFjZSBwbG90cyBkb24ndCBzaG93IG11bHRpbW9kYWxpdHkKCmBgYHtyIH0KbWNtY190cmFjZShkcmF3czFiLCByZWdleF9wYXJzPWMoJ3NpZ21hX2YxJywnbGVuZ3Roc2NhbGVfZjEnLCdzaWdtYScpKQpgYGAKCldlIGRyb3AgZ2xvYmFsIGludGVyY2VwdCBmcm9tIHRoZSByZXN0IG9mIHRoZSBtb2RlbHMsIGJ1dCBjb250aW51ZQp1c2luZyAoZWFybHkgc3RvcHBlZCkgb3B0aW1pemF0aW9uIHRvIGluaXRpYWxpemUgdGhlIHNhbXBsaW5nLgoKIyMjIE1vZGVsIDI6IFNsb3cgdHJlbmQgKyB5ZWFybHkgc2Vhc29uYWwgdHJlbmQKClRoZSBtb2RlbCAyIGFkZHMgeWVhcmx5IHNlYXNvbmFsIHRyZW5kIHVzaW5nIEdQIHdpdGggcGVyaW9kaWMKY292YXJpYW5jZSBmdW5jdGlvbi4KJCQKZiA9IFxtYm94e2ludGVyY2VwdH0gKyBmXzEgKyBmXzIgXFwKXG1ib3h7aW50ZXJjZXB0fSBcc2ltIFxtYm94e25vcm1hbH0oMCwxKVxcCmZfMSBcc2ltIFxtYm94e0dQfSgwLEtfMSlcXApmXzIgXHNpbSBcbWJveHtHUH0oMCxLXzIpCiQkCndoZXJlIHRoZSBmaXJzdCBHUCB1c2VzIHRoZSBleHBvbmVudGlhdGVkIHF1YWRyYXRpYyBjb3ZhcmlhbmNlCmZ1bmN0aW9uLCBhbmQgdGhlIHNlY29uZCBvbmUgYSBwZXJpb2RpYyBjb3ZhcmlhbmNlIGZ1bmN0aW9uLiBNb3N0CnllYXJzIGhhdmUgMzY1IGNhbGVuZGFyIGRheXMgYW5kIGV2ZXJ5IGZvdXIgeWVhcnMgKGR1cmluZyB0aGUgZGF0YQpyYW5nZSkgdGhlcmUgYXJlIDM2NiBkYXlzLCBhbmQgdGh1cyB3ZSBzaW1wbGlmeSBhbmQgdXNlIHBlcmlvZCBvZgozNjUuMjUgZm9yIHRoZSBwZXJpb2RpYyBjb21wb25lbnQsCgpUaGUgZmlyc3QgdmVyc2lvbiBvZiBtb2RlbCAyIHdpdGggdGhlIGFkZGVkIHBlcmlvZGljIGNvbXBvbmVudApmb2xsb3dpbmcgZnJvbSBSaXV0b3J0LU1heW9sICgyMDIwKSB0dXJuZWQgb3V0IGJlIHZlcnkgc2xvdy4gV2l0aAp0aGUgZGVmYXVsdCBNQ01DIG9wdGlvbnMgdGhlIGluZmVyZW5jZSB3b3VsZCBoYXZlIHRha2VuIGhvdXJzLCBidXQKd2l0aCB0aGUgc2hvcnQgY2hhaW5zIGl0IHdhcyBwb3NzaWJsZSB0byBpbmZlciB0aGF0IHNvbWV0aGluZyBoYXMKdG8gYmUgd3JvbmcuIFRoZSBtb2RlbCBvdXRwdXQgd2FzIHNlbnNpYmxlLCBidXQgZGlhZ25vc3RpY3MKaW5kaWNhdGVkIHZlcnkgc2xvdyBtaXhpbmcuIEJ5IG1vcmUgY2FyZWZ1bCBleGFtaW5hdGlvbiBvZiB0aGUKbW9kZWwgaXQgdHVybmVkIG91dCB0aGF0IHRoZSBwZXJpb2RpYyBjb21wb25lbnQgd2FzIGluY2x1ZGluZwphbm90aGVyIGludGVyY2VwdCB0ZXJtIGFuZCB3aXRoIHR3byBpbnRlcmNlcHQgdGVybXMgdGhlaXIgc3VtIHdhcwp3ZWxsIGluZm9ybWVkIGJ5IHRoZSBkYXRhLCBidXQgaW5kaXZpZHVhbGx5IHRoZXkgd2VyZSBub3Qgd2VsbAppbmZvcm1lZCBhbmQgdGh1cyB0aGUgcG9zdGVyaW9ycyB3ZXJlIHdpZGUsIHdoaWNoIGxlYWQgdG8gdmVyeSBzbG93Cm1peGluZy4gVGhpcyBiYWQgbW9kZWwgaXMgbm90IHNob3duIGhlcmUsIGJ1dCB0aGUgb3B0aW1pemF0aW9uLApzaG9ydCBNQ01DIGNoYWlucyBhbmQgc2FtcGxpbmcgZGlhZ25vc3RpYyB0b29scyB3ZXJlIGNydWNpYWwgZm9yCmZhc3QgZXhwZXJpbWVudGF0aW9uIGFuZCBzb2x2aW5nIHRoZSBwcm9ibGVtLgoKQ29tcGlsZSBTdGFuIG1vZGVsIDIgKHRoZSBmaXhlZCB2ZXJzaW9uKSBbZ3BiZjIuc3Rhbl0oaHR0cHM6Ly9naXRodWIuY29tL2F2ZWh0YXJpL2Nhc2VzdHVkaWVzL2Jsb2IvbWFzdGVyL0JpcnRoZGF5cy9ncGJmMi5zdGFuKQoKYGBge3IgbW9kZWwyLCByZXN1bHRzPSdoaWRlJ30KbW9kZWwyIDwtIGNtZHN0YW5fbW9kZWwoc3Rhbl9maWxlID0gcm9vdCgiQmlydGhkYXlzIiwgImdwYmYyLnN0YW4iKSwKICAgICAgICAgICAgICAgICAgICAgICAgaW5jbHVkZV9wYXRocyA9IHJvb3QoIkJpcnRoZGF5cyIpKQpgYGAKCkRhdGEgdG8gYmUgcGFzc2VkIHRvIFN0YW4KCmBgYHtyIH0Kc3RhbmRhdGEyIDwtIGxpc3QoeD1kYXRhJGlkLAogICAgICAgICAgICAgICAgICB5PWxvZyhkYXRhJGJpcnRoc19yZWxhdGl2ZTEwMCksCiAgICAgICAgICAgICAgICAgIE49bGVuZ3RoKGRhdGEkaWQpLAogICAgICAgICAgICAgICAgICBjX2YxPTEuNSwgIyBmYWN0b3IgYyBvZiBiYXNpcyBmdW5jdGlvbnMgZm9yIEdQIGZvciBmMQogICAgICAgICAgICAgICAgICBNX2YxPTIwLCAgIyBudW1iZXIgb2YgYmFzaXMgZnVuY3Rpb25zIGZvciBHUCBmb3IgZjEKICAgICAgICAgICAgICAgICAgSl9mMj0yMCkgICMgbnVtYmVyIG9mIGJhc2lzIGZ1bmN0aW9ucyBmb3IgcGVyaW9kaWMgZjIKYGBgCgpPcHRpbWl6aW5nIGlzIGZhc3RlciB0aGFuIHNhbXBsaW5nIChhbHRob3VnaCB0aGlzIHJlc3VsdCBjYW4gYmUKdXNlZnVsIGluIGEgcXVpY2sgd29ya2Zsb3csIHRoZSByZXN1bHQgc2hvdWxkIG5vdCBiZSB1c2VkIGFzIHRoZQpmaW5hbCByZXN1bHQpLgoKYGBge3Igb3B0MiwgcmVzdWx0cz0naGlkZSd9Cm9wdDIgPC0gbW9kZWwyJG9wdGltaXplKGRhdGE9c3RhbmRhdGEyLCBpbml0PTAsIGFsZ29yaXRobT0nYmZncycpCmBgYAoKQ2hlY2sgd2hldGhlciBwYXJhbWV0ZXJzIGhhdmUgcmVhc29uYWJsZSB2YWx1ZXMKCmBgYHtyIH0Kb2RyYXdzMiA8LSBvcHQyJGRyYXdzKCkKc3Vic2V0KG9kcmF3czIsIHZhcmlhYmxlPWMoJ3NpZ21hXycsJ2xlbmd0aHNjYWxlXycsJ3NpZ21hJyksIHJlZ2V4PVRSVUUpCmBgYAoKQ29tcGFyZSB0aGUgbW9kZWwgdG8gdGhlIGRhdGEKCmBgYHtyIH0KRWYgPC0gZXhwKGFzLm51bWVyaWMoc3Vic2V0KG9kcmF3czIsIHZhcmlhYmxlPSdmJykpKQpFZjEgPC0gYXMubnVtZXJpYyhzdWJzZXQob2RyYXdzMiwgdmFyaWFibGU9J2YxJykpCkVmMSA8LSBleHAoRWYxIC0gbWVhbihFZjEpICsgbWVhbihsb2coZGF0YSRiaXJ0aHNfcmVsYXRpdmUxMDApKSkKRWYyIDwtIGFzLm51bWVyaWMoc3Vic2V0KG9kcmF3czIsIHZhcmlhYmxlPSdmMicpKQpFZjIgPC0gZXhwKEVmMiAtIG1lYW4oRWYyKSArIG1lYW4obG9nKGRhdGEkYmlydGhzX3JlbGF0aXZlMTAwKSkpCnBmIDwtIGRhdGEgJT4lCiAgbXV0YXRlKEVmID0gRWYpICU+JQogIGdncGxvdChhZXMoeD1kYXRlLCB5PWJpcnRoc19yZWxhdGl2ZTEwMCkpICsgZ2VvbV9wb2ludChjb2xvcj1zZXQxWzJdLCBhbHBoYT0wLjIpICsKICBnZW9tX2xpbmUoYWVzKHk9RWYpLCBjb2xvcj1zZXQxWzFdKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PTEwMCwgY29sb3I9J2dyYXknKSArCiAgbGFicyh4PSJEYXRlIiwgeT0iUmVsYXRpdmUgbnVtYmVyIG9mIGJpcnRocyIpCnBmMSA8LSBkYXRhICU+JQogIG11dGF0ZShFZjEgPSBFZjEpICU+JQogIGdncGxvdChhZXMoeD1kYXRlLCB5PWJpcnRoc19yZWxhdGl2ZTEwMCkpICsgZ2VvbV9wb2ludChjb2xvcj1zZXQxWzJdLCBhbHBoYT0wLjIpICsKICBnZW9tX2xpbmUoYWVzKHk9RWYxKSwgY29sb3I9c2V0MVsxXSkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdD0xMDAsIGNvbG9yPSdncmF5JykgKwogIGxhYnMoeD0iRGF0ZSIsIHk9IlJlbGF0aXZlIG51bWJlciBvZiBiaXJ0aHMiKQpwZjIgPC0gZGF0YSAlPiUKICBtdXRhdGUoRWYyID0gRWYyKSAlPiUKICBncm91cF9ieShkYXlfb2ZfeWVhcjIpICU+JQogIHN1bW1hcmlzZShtZWFuYmlydGhzPW1lYW4oYmlydGhzX3JlbGF0aXZlMTAwKSwgbWVhbkVmMj1tZWFuKEVmMikpICU+JQogIGdncGxvdChhZXMoeD1hcy5EYXRlKCIxOTg3LTEyLTMxIikrZGF5X29mX3llYXIyLCB5PW1lYW5iaXJ0aHMpKSArIGdlb21fcG9pbnQoY29sb3I9c2V0MVsyXSwgYWxwaGE9MC4yKSArCiAgc2NhbGVfeF9kYXRlKGRhdGVfYnJlYWtzID0gIjEgbW9udGgiLCBkYXRlX2xhYmVscyA9ICIlYiIpICsKICBnZW9tX2xpbmUoYWVzKHk9bWVhbkVmMiksIGNvbG9yPXNldDFbMV0pICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9MTAwLCBjb2xvcj0nZ3JheScpICsKICBsYWJzKHg9IkRhdGUiLCB5PSJSZWxhdGl2ZSBudW1iZXIgb2YgYmlydGhzIG9mIHllYXIiKQpwZiAvIChwZjEgKyBwZjIpCmBgYAoKU2FtcGxlIHNob3J0IGNoYWlucyB1c2luZyB0aGUgb3B0aW1pemF0aW9uIHJlc3VsdCBhcyBpbml0aWFsIHZhbHVlcwooYWx0aG91Z2ggdGhlIHJlc3VsdCBmcm9tIHNob3J0IGNoYWlucyBjYW4gYmUgdXNlZnVsIGluIGEgcXVpY2sKd29ya2Zsb3csIHRoZSByZXN1bHQgc2hvdWxkIG5vdCBiZSB1c2VkIGFzIHRoZSBmaW5hbCByZXN1bHQpLgoKYGBge3IgfQppbml0MiA8LSBzYXBwbHkoYygnbGVuZ3Roc2NhbGVfZjEnLCdsZW5ndGhzY2FsZV9mMicsJ3NpZ21hX2YxJywnc2lnbWFfZjInLCdzaWdtYScsJ2JldGFfZjEnLCdiZXRhX2YyJyksCiAgICAgICAgICAgICAgICBmdW5jdGlvbih2YXJpYWJsZSkge2FzLm51bWVyaWMoc3Vic2V0KG9kcmF3czIsIHZhcmlhYmxlPXZhcmlhYmxlKSl9KQpgYGAKYGBge3IgZml0MiwgcmVzdWx0cz0naGlkZSd9CmZpdDIgPC0gbW9kZWwyJHNhbXBsZShkYXRhPXN0YW5kYXRhMiwgaXRlcl93YXJtdXA9MTAwLCBpdGVyX3NhbXBsaW5nPTEwMCwKICAgICAgICAgICAgICAgICAgICAgIGNoYWlucz00LCBwYXJhbGxlbF9jaGFpbnM9NCwKICAgICAgICAgICAgICAgICAgICAgIGluaXQ9ZnVuY3Rpb24oKSB7IGluaXQyIH0pCmBgYAoKQ2hlY2sgd2hldGhlciBwYXJhbWV0ZXJzIGhhdmUgcmVhc29uYWJsZSB2YWx1ZXMKCmBgYHtyIH0KZHJhd3MyIDwtIGZpdDIkZHJhd3MoKQpzdW1tYXJpc2VfZHJhd3Moc3Vic2V0KGRyYXdzMiwgdmFyaWFibGU9Yygnc2lnbWFfJywnbGVuZ3Roc2NhbGVfJywnc2lnbWEnKSwgcmVnZXg9VFJVRSkpCmBgYAoKQ29tcGFyZSB0aGUgbW9kZWwgdG8gdGhlIGRhdGEKCmBgYHtyIH0KZHJhd3MyIDwtIGFzX2RyYXdzX21hdHJpeChkcmF3czIpCkVmIDwtIGV4cChhcHBseShzdWJzZXQoZHJhd3MyLCB2YXJpYWJsZT0nZicpLCAyLCBtZWRpYW4pKQpFZjEgPC0gYXBwbHkoc3Vic2V0KGRyYXdzMiwgdmFyaWFibGU9J2YxJyksIDIsIG1lZGlhbikKRWYxIDwtIGV4cChFZjEgLSBtZWFuKEVmMSkgKyBtZWFuKGxvZyhkYXRhJGJpcnRoc19yZWxhdGl2ZTEwMCkpKQpFZjIgPC0gYXBwbHkoc3Vic2V0KGRyYXdzMiwgdmFyaWFibGU9J2YyJyksIDIsIG1lZGlhbikKRWYyIDwtIGV4cChFZjIgLSBtZWFuKEVmMikgKyBtZWFuKGxvZyhkYXRhJGJpcnRoc19yZWxhdGl2ZTEwMCkpKQpwZiA8LSBkYXRhICU+JQogIG11dGF0ZShFZiA9IEVmKSAlPiUKICBnZ3Bsb3QoYWVzKHg9ZGF0ZSwgeT1iaXJ0aHNfcmVsYXRpdmUxMDApKSArIGdlb21fcG9pbnQoY29sb3I9c2V0MVsyXSwgYWxwaGE9MC4yKSArCiAgZ2VvbV9saW5lKGFlcyh5PUVmKSwgY29sb3I9c2V0MVsxXSkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdD0xMDAsIGNvbG9yPSdncmF5JykgKwogIGxhYnMoeD0iRGF0ZSIsIHk9IlJlbGF0aXZlIG51bWJlciBvZiBiaXJ0aHMiKQpwZjEgPC0gZGF0YSAlPiUKICBtdXRhdGUoRWYxID0gRWYxKSAlPiUKICBnZ3Bsb3QoYWVzKHg9ZGF0ZSwgeT1iaXJ0aHNfcmVsYXRpdmUxMDApKSArIGdlb21fcG9pbnQoY29sb3I9c2V0MVsyXSwgYWxwaGE9MC4yKSArCiAgZ2VvbV9saW5lKGFlcyh5PUVmMSksIGNvbG9yPXNldDFbMV0pICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9MTAwLCBjb2xvcj0nZ3JheScpICsKICBsYWJzKHg9IkRhdGUiLCB5PSJSZWxhdGl2ZSBudW1iZXIgb2YgYmlydGhzIikKcGYyIDwtIGRhdGEgJT4lCiAgbXV0YXRlKEVmMiA9IEVmMikgJT4lCiAgZ3JvdXBfYnkoZGF5X29mX3llYXIyKSAlPiUKICBzdW1tYXJpc2UobWVhbmJpcnRocz1tZWFuKGJpcnRoc19yZWxhdGl2ZTEwMCksIG1lYW5FZjI9bWVhbihFZjIpKSAlPiUKICBnZ3Bsb3QoYWVzKHg9YXMuRGF0ZSgiMTk4Ny0xMi0zMSIpK2RheV9vZl95ZWFyMiwgeT1tZWFuYmlydGhzKSkgKyBnZW9tX3BvaW50KGNvbG9yPXNldDFbMl0sIGFscGhhPTAuMikgKwogIHNjYWxlX3hfZGF0ZShkYXRlX2JyZWFrcyA9ICIxIG1vbnRoIiwgZGF0ZV9sYWJlbHMgPSAiJWIiKSArCiAgZ2VvbV9saW5lKGFlcyh5PW1lYW5FZjIpLCBjb2xvcj1zZXQxWzFdKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PTEwMCwgY29sb3I9J2dyYXknKSArCiAgbGFicyh4PSJEYXRlIiwgeT0iUmVsYXRpdmUgbnVtYmVyIG9mIGJpcnRocyBvZiB5ZWFyIikKcGYgLyAocGYxICsgcGYyKQpgYGAKClNlYXNvbmFsIGNvbXBvbmVudCBoYXMgcmVhc29uYWJsZSBmaXQgdG8gdGhlIGRhdGEuCgojIyMgTW9kZWwgMzogU2xvdyB0cmVuZCArIHllYXJseSBzZWFzb25hbCB0cmVuZCArIGRheSBvZiB3ZWVrCgpCYXNlZCBvbiB0aGUgcXVpY2sgcGxvdHRpbmcgb2YgdGhlIGRhdGEgYWJvdmUsIGRheSBvZiB3ZWVrIGhhcyBhCmNsZWFyIGVmZmVjdCBhbmQgdGhlcmUgYXJlIGxlc3MgYmFiaWVzIGJvcm4gb24gU2F0dXJkYXkgYW5kClN1bmRheS4gVGhpcyBjYW4gYmUgdGFrZW4gaW50byBhY2NvdW50IHdpdGggc2ltcGxlIGFkZGl0aXZlCmNvZWZmaWNpZW50cy4gV2UgZml4IHRoZSBlZmZlY3Qgb2YgTW9uZGF5IHRvIDAgYW5kIGhhdmUgYWRkaXRpb25hbApjb2VmZmljaWVudHMgZm9yIG90aGVyIHdlZWtkYXlzLgokJApmID0gXG1ib3h7aW50ZXJjZXB0fSArIGZfMSArIGZfMiArIFxiZXRhX3tcbWJveHtkYXkgb2Ygd2Vla319IFxcClxtYm94e2ludGVyY2VwdH0gXHNpbSBcbWJveHtub3JtYWx9KDAsMSlcXApmXzEgXHNpbSBcbWJveHtHUH0oMCxLXzEpXFwKZl8yIFxzaW0gXG1ib3h7R1B9KDAsS18yKVxcClxiZXRhX3tcbWJveHtkYXkgb2Ygd2Vla319ID0gMCBccXVhZCBcbWJveHtpZiBkYXkgb2Ygd2VlayBpcyBNb25kYXl9XFwKXGJldGFfe1xtYm94e2RheSBvZiB3ZWVrfX0gXHNpbSBcbWJveHtub3JtYWx9KDAsMSkgXHF1YWQgXG1ib3h7aWYgZGF5IG9mIHdlZWsgaXMgbm90IE1vbmRheX0KJCQKCkNvbXBpbGUgU3RhbiBtb2RlbCAzIFtncGJmMy5zdGFuXShodHRwczovL2dpdGh1Yi5jb20vYXZlaHRhcmkvY2FzZXN0dWRpZXMvYmxvYi9tYXN0ZXIvQmlydGhkYXlzL2dwYmYzLnN0YW4pCgpgYGB7ciBtb2RlbDMsIHJlc3VsdHM9J2hpZGUnfQptb2RlbDMgPC0gY21kc3Rhbl9tb2RlbChzdGFuX2ZpbGUgPSByb290KCJCaXJ0aGRheXMiLCAiZ3BiZjMuc3RhbiIpLAogICAgICAgICAgICAgICAgICAgICAgICBpbmNsdWRlX3BhdGhzID0gcm9vdCgiQmlydGhkYXlzIikpCmBgYAoKRGF0YSB0byBiZSBwYXNzZWQgdG8gU3RhbgoKYGBge3IgfQpzdGFuZGF0YTMgPC0gbGlzdCh4PWRhdGEkaWQsCiAgICAgICAgICAgICAgICAgIHk9bG9nKGRhdGEkYmlydGhzX3JlbGF0aXZlMTAwKSwKICAgICAgICAgICAgICAgICAgTj1sZW5ndGgoZGF0YSRpZCksCiAgICAgICAgICAgICAgICAgIGNfZjE9MS41LCAjIGZhY3RvciBjIG9mIGJhc2lzIGZ1bmN0aW9ucyBmb3IgR1AgZm9yIGYxCiAgICAgICAgICAgICAgICAgIE1fZjE9MjAsICAjIG51bWJlciBvZiBiYXNpcyBmdW5jdGlvbnMgZm9yIEdQIGZvciBmMQogICAgICAgICAgICAgICAgICBKX2YyPTIwLCAgIyBudW1iZXIgb2YgYmFzaXMgZnVuY3Rpb25zIGZvciBwZXJpb2RpYyBmMgogICAgICAgICAgICAgICAgICBkYXlfb2Zfd2Vlaz1kYXRhJGRheV9vZl93ZWVrKQpgYGAKCk9wdGltaXppbmcgaXMgZmFzdGVyIHRoYW4gc2FtcGxpbmcgKGFsdGhvdWdoIHRoaXMgcmVzdWx0IGNhbiBiZQp1c2VmdWwgaW4gYSBxdWljayB3b3JrZmxvdywgdGhlIHJlc3VsdCBzaG91bGQgbm90IGJlIHVzZWQgYXMgdGhlCmZpbmFsIHJlc3VsdCkuCgpgYGB7ciBvcHQzLCByZXN1bHRzPSdoaWRlJ30Kb3B0MyA8LSBtb2RlbDMkb3B0aW1pemUoZGF0YT1zdGFuZGF0YTMsIGluaXQ9MCwgYWxnb3JpdGhtPSdiZmdzJykKYGBgCgpDaGVjayB3aGV0aGVyIHBhcmFtZXRlcnMgaGF2ZSByZWFzb25hYmxlIHZhbHVlcwoKYGBge3IgfQpvZHJhd3MzIDwtIG9wdDMkZHJhd3MoKQpzdWJzZXQob2RyYXdzMywgdmFyaWFibGU9Yygnc2lnbWFfJywnbGVuZ3Roc2NhbGVfJywnc2lnbWEnKSwgcmVnZXg9VFJVRSkKc3Vic2V0KG9kcmF3czMsIHZhcmlhYmxlPWMoJ2JldGFfZjMnKSkKYGBgCgpDb21wYXJlIHRoZSBtb2RlbCB0byB0aGUgZGF0YQoKYGBge3IgfQpFZiA8LSBleHAoYXMubnVtZXJpYyhzdWJzZXQob2RyYXdzMywgdmFyaWFibGU9J2YnKSkpCkVmMSA8LSBhcy5udW1lcmljKHN1YnNldChvZHJhd3MzLCB2YXJpYWJsZT0nZjEnKSkKRWYxIDwtIGV4cChFZjEgLSBtZWFuKEVmMSkgKyBtZWFuKGxvZyhkYXRhJGJpcnRoc19yZWxhdGl2ZTEwMCkpKQpFZjIgPC0gYXMubnVtZXJpYyhzdWJzZXQob2RyYXdzMywgdmFyaWFibGU9J2YyJykpCkVmMiA8LSBleHAoRWYyIC0gbWVhbihFZjIpICsgbWVhbihsb2coZGF0YSRiaXJ0aHNfcmVsYXRpdmUxMDApKSkKRWZfZGF5X29mX3dlZWsgPC0gYXMubnVtZXJpYyhzdWJzZXQob2RyYXdzMywgdmFyaWFibGU9J2ZfZGF5X29mX3dlZWsnKSkKRWZfZGF5X29mX3dlZWsgPC0gZXhwKEVmX2RheV9vZl93ZWVrIC0gbWVhbihFZl9kYXlfb2Zfd2VlaykgKyBtZWFuKGxvZyhkYXRhJGJpcnRoc19yZWxhdGl2ZTEwMCkpKQpwZiA8LSBkYXRhICU+JQogIG11dGF0ZShFZiA9IEVmKSAlPiUKICBnZ3Bsb3QoYWVzKHg9ZGF0ZSwgeT1iaXJ0aHNfcmVsYXRpdmUxMDApKSArIGdlb21fcG9pbnQoY29sb3I9c2V0MVsyXSwgYWxwaGE9MC4yKSArCiAgZ2VvbV9saW5lKGFlcyh5PUVmKSwgY29sb3I9c2V0MVsxXSwgYWxwaGE9MC43NSkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdD0xMDAsIGNvbG9yPSdncmF5JykgKwogIGxhYnMoeD0iRGF0ZSIsIHk9IlJlbGF0aXZlIG51bWJlciBvZiBiaXJ0aHMiKQpwZjEgPC0gZGF0YSAlPiUKICBtdXRhdGUoRWYxID0gRWYxKSAlPiUKICBnZ3Bsb3QoYWVzKHg9ZGF0ZSwgeT1iaXJ0aHNfcmVsYXRpdmUxMDApKSArIGdlb21fcG9pbnQoY29sb3I9c2V0MVsyXSwgYWxwaGE9MC4yKSArCiAgZ2VvbV9saW5lKGFlcyh5PUVmMSksIGNvbG9yPXNldDFbMV0pICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9MTAwLCBjb2xvcj0nZ3JheScpICsKICBsYWJzKHg9IkRhdGUiLCB5PSJSZWxhdGl2ZSBudW1iZXIgb2YgYmlydGhzIikKcGYyIDwtIGRhdGEgJT4lCiAgbXV0YXRlKEVmMiA9IEVmMikgJT4lCiAgZ3JvdXBfYnkoZGF5X29mX3llYXIyKSAlPiUKICBzdW1tYXJpc2UobWVhbmJpcnRocz1tZWFuKGJpcnRoc19yZWxhdGl2ZTEwMCksIG1lYW5FZjI9bWVhbihFZjIpKSAlPiUKICBnZ3Bsb3QoYWVzKHg9YXMuRGF0ZSgiMTk4Ny0xMi0zMSIpK2RheV9vZl95ZWFyMiwgeT1tZWFuYmlydGhzKSkgKyBnZW9tX3BvaW50KGNvbG9yPXNldDFbMl0sIGFscGhhPTAuMikgKwogIHNjYWxlX3hfZGF0ZShkYXRlX2JyZWFrcyA9ICIxIG1vbnRoIiwgZGF0ZV9sYWJlbHMgPSAiJWIiKSArCiAgZ2VvbV9saW5lKGFlcyh5PW1lYW5FZjIpLCBjb2xvcj1zZXQxWzFdKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PTEwMCwgY29sb3I9J2dyYXknKSArCiAgbGFicyh4PSJEYXRlIiwgeT0iUmVsYXRpdmUgbnVtYmVyIG9mIGJpcnRocyBvZiB5ZWFyIikKcGYzIDwtIGdncGxvdChkYXRhPWRhdGEsIGFlcyh4PWRheV9vZl93ZWVrLCB5PWJpcnRoc19yZWxhdGl2ZTEwMCkpICsgZ2VvbV9wb2ludChjb2xvcj1zZXQxWzJdLCBhbHBoYT0wLjIpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gMTo3LCBsYWJlbHM9YygnTW9uJywnVHVlJywnV2VkJywnVGh1JywnRnJpJywnU2F0JywnU3VuJykpICsKICBnZW9tX2xpbmUoZGF0YT1kYXRhLmZyYW1lKHg9MTo3LHk9RWZfZGF5X29mX3dlZWspLCBhZXMoeD14LCB5PUVmX2RheV9vZl93ZWVrKSwgY29sb3I9c2V0MVsxXSkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdD0xMDAsIGNvbG9yPSdncmF5JykgKwogIGxhYnMoeD0iRGF0ZSIsIHk9IlJlbGF0aXZlIG51bWJlciBvZiBiaXJ0aHMgb2Ygd2VlayIpCihwZiArIHBmMSkgLyAocGYyICsgcGYzKQpgYGAKClNhbXBsZSBzaG9ydCBjaGFpbnMgdXNpbmcgdGhlIG9wdGltaXphdGlvbiByZXN1bHQgYXMgaW5pdGlhbCB2YWx1ZXMKKGFsdGhvdWdoIHRoZSByZXN1bHQgZnJvbSBzaG9ydCBjaGFpbnMgY2FuIGJlIHVzZWZ1bCBpbiBhIHF1aWNrCndvcmtmbG93LCB0aGUgcmVzdWx0IHNob3VsZCBub3QgYmUgdXNlZCBhcyB0aGUgZmluYWwgcmVzdWx0KS4KCmBgYHtyIH0KaW5pdDMgPC0gc2FwcGx5KGMoJ2xlbmd0aHNjYWxlX2YxJywnbGVuZ3Roc2NhbGVfZjInLCdzaWdtYV9mMScsJ3NpZ21hX2YyJywnc2lnbWEnLAogICAgICAgICAgICAgICAgICAnYmV0YV9mMScsJ2JldGFfZjInLCdiZXRhX2YzJyksCiAgICAgICAgICAgICAgICBmdW5jdGlvbih2YXJpYWJsZSkge2FzLm51bWVyaWMoc3Vic2V0KG9kcmF3czMsIHZhcmlhYmxlPXZhcmlhYmxlKSl9KQpgYGAKYGBge3IgZml0MywgcmVzdWx0cz0naGlkZSd9CmZpdDMgPC0gbW9kZWwzJHNhbXBsZShkYXRhPXN0YW5kYXRhMywgaXRlcl93YXJtdXA9MTAwLCBpdGVyX3NhbXBsaW5nPTEwMCwKICAgICAgICAgICAgICAgICAgICAgIGNoYWlucz00LCBwYXJhbGxlbF9jaGFpbnM9NCwKICAgICAgICAgICAgICAgICAgICAgIGluaXQ9ZnVuY3Rpb24oKSB7IGluaXQzIH0pCmBgYAoKQ2hlY2sgd2hldGhlciBwYXJhbWV0ZXJzIGhhdmUgcmVhc29uYWJsZSB2YWx1ZXMKCmBgYHtyIH0KZHJhd3MzIDwtIGZpdDMkZHJhd3MoKQpzdW1tYXJpc2VfZHJhd3Moc3Vic2V0KGRyYXdzMywgdmFyaWFibGU9Yygnc2lnbWFfJywnbGVuZ3Roc2NhbGVfJywnc2lnbWEnKSwgcmVnZXg9VFJVRSkpCnN1bW1hcmlzZV9kcmF3cyhzdWJzZXQoZHJhd3MzLCB2YXJpYWJsZT1jKCdiZXRhX2YzJykpKQpgYGAKCkNvbXBhcmUgdGhlIG1vZGVsIHRvIHRoZSBkYXRhCgpgYGB7ciB9CmRyYXdzMyA8LSBhc19kcmF3c19tYXRyaXgoZHJhd3MzKQpFZiA8LSBleHAoYXBwbHkoc3Vic2V0KGRyYXdzMywgdmFyaWFibGU9J2YnKSwgMiwgbWVkaWFuKSkKRWYxIDwtIGFwcGx5KHN1YnNldChkcmF3czMsIHZhcmlhYmxlPSdmMScpLCAyLCBtZWRpYW4pCkVmMSA8LSBleHAoRWYxIC0gbWVhbihFZjEpICsgbWVhbihsb2coZGF0YSRiaXJ0aHNfcmVsYXRpdmUxMDApKSkKRWYyIDwtIGFwcGx5KHN1YnNldChkcmF3czMsIHZhcmlhYmxlPSdmMicpLCAyLCBtZWRpYW4pCkVmMiA8LSBleHAoRWYyIC0gbWVhbihFZjIpICsgbWVhbihsb2coZGF0YSRiaXJ0aHNfcmVsYXRpdmUxMDApKSkKRWZfZGF5X29mX3dlZWsgPC0gYXBwbHkoc3Vic2V0KGRyYXdzMywgdmFyaWFibGU9J2ZfZGF5X29mX3dlZWsnKSwgMiwgbWVkaWFuKQpFZl9kYXlfb2Zfd2VlayA8LSBleHAoRWZfZGF5X29mX3dlZWsgLSBtZWFuKEVmX2RheV9vZl93ZWVrKSArIG1lYW4obG9nKGRhdGEkYmlydGhzX3JlbGF0aXZlMTAwKSkpCnBmIDwtIGRhdGEgJT4lCiAgbXV0YXRlKEVmID0gRWYpICU+JQogIGdncGxvdChhZXMoeD1kYXRlLCB5PWJpcnRoc19yZWxhdGl2ZTEwMCkpICsgZ2VvbV9wb2ludChjb2xvcj1zZXQxWzJdLCBhbHBoYT0wLjIpICsKICBnZW9tX2xpbmUoYWVzKHk9RWYpLCBjb2xvcj1zZXQxWzFdLCBhbHBoYT0wLjc1KSArCiAgbGFicyh4PSJEYXRlIiwgeT0iUmVsYXRpdmUgbnVtYmVyIG9mIGJpcnRocyIpCnBmMSA8LSBkYXRhICU+JQogIG11dGF0ZShFZjEgPSBFZjEpICU+JQogIGdncGxvdChhZXMoeD1kYXRlLCB5PWJpcnRoc19yZWxhdGl2ZTEwMCkpICsgZ2VvbV9wb2ludChjb2xvcj1zZXQxWzJdLCBhbHBoYT0wLjIpICsKICBnZW9tX2xpbmUoYWVzKHk9RWYxKSwgY29sb3I9c2V0MVsxXSkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdD0xMDAsIGNvbG9yPSdncmF5JykgKwogIGxhYnMoeD0iRGF0ZSIsIHk9IlJlbGF0aXZlIG51bWJlciBvZiBiaXJ0aHMiKQpwZjIgPC0gZGF0YSAlPiUKICBtdXRhdGUoRWYyID0gRWYyKSAlPiUKICBncm91cF9ieShkYXlfb2ZfeWVhcjIpICU+JQogIHN1bW1hcmlzZShtZWFuYmlydGhzPW1lYW4oYmlydGhzX3JlbGF0aXZlMTAwKSwgbWVhbkVmMj1tZWFuKEVmMikpICU+JQogIGdncGxvdChhZXMoeD1hcy5EYXRlKCIxOTg3LTEyLTMxIikrZGF5X29mX3llYXIyLCB5PW1lYW5iaXJ0aHMpKSArIGdlb21fcG9pbnQoY29sb3I9c2V0MVsyXSwgYWxwaGE9MC4yKSArCiAgc2NhbGVfeF9kYXRlKGRhdGVfYnJlYWtzID0gIjEgbW9udGgiLCBkYXRlX2xhYmVscyA9ICIlYiIpICsKICBnZW9tX2xpbmUoYWVzKHk9bWVhbkVmMiksIGNvbG9yPXNldDFbMV0pICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9MTAwLCBjb2xvcj0nZ3JheScpICsKICBsYWJzKHg9IkRhdGUiLCB5PSJSZWxhdGl2ZSBudW1iZXIgb2YgYmlydGhzIG9mIHllYXIiKQpwZjMgPC0gZ2dwbG90KGRhdGE9ZGF0YSwgYWVzKHg9ZGF5X29mX3dlZWssIHk9YmlydGhzX3JlbGF0aXZlMTAwKSkgKyBnZW9tX3BvaW50KGNvbG9yPXNldDFbMl0sIGFscGhhPTAuMikgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSAxOjcsIGxhYmVscz1jKCdNb24nLCdUdWUnLCdXZWQnLCdUaHUnLCdGcmknLCdTYXQnLCdTdW4nKSkgKwogIGdlb21fbGluZShkYXRhPWRhdGEuZnJhbWUoeD0xOjcseT1FZl9kYXlfb2Zfd2VlayksIGFlcyh4PXgsIHk9RWZfZGF5X29mX3dlZWspLCBjb2xvcj1zZXQxWzFdKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PTEwMCwgY29sb3I9J2dyYXknKSArCiAgbGFicyh4PSJEYXRlIiwgeT0iUmVsYXRpdmUgbnVtYmVyIG9mIGJpcnRocyBvZiB3ZWVrIikKKHBmICsgcGYxKSAvIChwZjIgKyBwZjMpCmBgYAoKV2Vla2RheSBlZmZlY3RzIGFyZSBlYXN5IHRvIGVzdGltYXRlIGFzIHRoZXJlIGFyZSBhYm91dCB0aG91c2FuZApvYnNlcnZhdGlvbnMgcGVyIHdlZWtkYXkuCgojIyMgTW9kZWwgNDogbG9uZyB0ZXJtIHNtb290aCArIHNlYXNvbmFsICsgd2Vla2RheSB3aXRoIGluY3JlYXNpbmcgbWFnbml0dWRlCgpMb29raW5nIGF0IHRoZSB0aW1lIHNlcmllcyBvZiB3aG9sZSBkYXRhIHdlIHNlZSB0aGUgZG90cwpyZXByZXNlbnRpbmcgdGhlIGRhaWx5IHZhbHVlcyBmb3JtaW5nIHRocmVlIGJyYW5jaGVzIHRoYXQgYXJlCmdldHRpbmcgZnVydGhlciBhd2F5IGZyb20gZWFjaCBvdGhlci4gSW4gcHJldmlvdXMgYW5hbHlzaXMgKEJEQTMpCndlIGFsc28gaGFkIGEgbW9kZWwgY29tcG9uZW50IGFsbG93aW5nIGdyYWR1YWxseSBjaGFuZ2luZyBlZmZlY3QKZm9yIGRheSBvZiB3ZWVrIGFuZCBkaWQgb2JzZXJ2ZSB0aGF0IHRoZSBlZmZlY3Qgb2YgU2F0dXJkYXkgYW5kClN1bmRheSBkaWQgZ2V0IHN0cm9uZ2VyIGluIHRpbWUuIFRoZSBuZXh0IG1vZGVsIGluY2x1ZGVzIHRpbWUKZGVwZW5kZW50IG1hZ25pdHVkZSBjb21wb25lbnQgZm9yIHRoZSBkYXkgb2Ygd2VlayBlZmZlY3QuCiQkCmYgPSBcbWJveHtpbnRlcmNlcHR9ICsgZl8xICsgZl8yICsgXGV4cChnXzMpXGJldGFfe1xtYm94e2RheSBvZiB3ZWVrfX0gXFwKXG1ib3h7aW50ZXJjZXB0fSBcc2ltIFxtYm94e25vcm1hbH0oMCwxKVxcCmZfMSBcc2ltIFxtYm94e0dQfSgwLEtfMSlcXApmXzIgXHNpbSBcbWJveHtHUH0oMCxLXzIpXFwKZ18zIFxzaW0gXG1ib3h7R1B9KDAsS18zKVxcClxiZXRhX3tcbWJveHtkYXkgb2Ygd2Vla319ID0gMCBccXVhZCBcbWJveHtpZiBkYXkgb2Ygd2VlayBpcyBNb25kYXl9XFwKXGJldGFfe1xtYm94e2RheSBvZiB3ZWVrfX0gXHNpbSBcbWJveHtub3JtYWx9KDAsMSkgXHF1YWQgXG1ib3h7aWYgZGF5IG9mIHdlZWsgaXMgbm90IE1vbmRheX0KJCQKVGhlIG1hZ25pdHVkZSBvZiB0aGUgd2Vla2RheSBlZmZlY3QgaXMgbW9kZWxsZWQgd2l0aCAkXGV4cChnXzMpJCwKd2hlcmUgJGdfMyQgaGFzIEdQIHByaW9yIHdpdGggemVybyBtZWFuIGFuZCBleHBvbmVudGlhdGVkIHF1YWRyYXRpYwpjb3ZhcmlhbmNlIGZ1bmN0aW9uLgoKQ29tcGlsZSBTdGFuIG1vZGVsIDQgW2dwYmY0LnN0YW5dKGh0dHBzOi8vZ2l0aHViLmNvbS9hdmVodGFyaS9jYXNlc3R1ZGllcy9ibG9iL21hc3Rlci9CaXJ0aGRheXMvZ3BiZjQuc3RhbikKCmBgYHtyIG1vZGVsNCwgcmVzdWx0cz0naGlkZSd9Cm1vZGVsNCA8LSBjbWRzdGFuX21vZGVsKHN0YW5fZmlsZSA9IHJvb3QoIkJpcnRoZGF5cyIsICJncGJmNC5zdGFuIiksCiAgICAgICAgICAgICAgICAgICAgICAgIGluY2x1ZGVfcGF0aHMgPSByb290KCJCaXJ0aGRheXMiKSkKYGBgCgpEYXRhIHRvIGJlIHBhc3NlZCB0byBTdGFuCgpgYGB7ciB9CnN0YW5kYXRhNCA8LSBsaXN0KHg9ZGF0YSRpZCwKICAgICAgICAgICAgICAgICAgeT1sb2coZGF0YSRiaXJ0aHNfcmVsYXRpdmUxMDApLAogICAgICAgICAgICAgICAgICBOPWxlbmd0aChkYXRhJGlkKSwKICAgICAgICAgICAgICAgICAgY19mMT0xLjUsICMgZmFjdG9yIGMgb2YgYmFzaXMgZnVuY3Rpb25zIGZvciBHUCBmb3IgZjEKICAgICAgICAgICAgICAgICAgTV9mMT0yMCwgICMgbnVtYmVyIG9mIGJhc2lzIGZ1bmN0aW9ucyBmb3IgR1AgZm9yIGYxCiAgICAgICAgICAgICAgICAgIEpfZjI9MjAsICAjIG51bWJlciBvZiBiYXNpcyBmdW5jdGlvbnMgZm9yIHBlcmlvZGljIGYyCiAgICAgICAgICAgICAgICAgIGNfZzM9MS41LCAjIGZhY3RvciBjIG9mIGJhc2lzIGZ1bmN0aW9ucyBmb3IgR1AgZm9yIGczCiAgICAgICAgICAgICAgICAgIE1fZzM9NSwgICAjIG51bWJlciBvZiBiYXNpcyBmdW5jdGlvbnMgZm9yIEdQIGZvciBnMwogICAgICAgICAgICAgICAgICBkYXlfb2Zfd2Vlaz1kYXRhJGRheV9vZl93ZWVrKSAKYGBgCgpBcyB3ZSBoYXZlIGluY3JlYXNlZCB0aGUgY29tcGxleGl0eSBvZiB0aGUgbW9kZWwsIHRoZSBtb2RlIHN0YXJ0cwp0byBiZSBsZXNzIGFuZCBsZXNzIHJlcHJlc2VudGF0aXZlIG9mIHRoZSBwb3N0ZXJpb3IuIFdlIHN0aWxsIHVzZQp0aGUgb3B0aW1pemF0aW9uIHRvIGNoZWNrIHRoYXQgY29kZSByZXR1cm5zIHNvbWV0aGluZyByZWFzb25hYmxlCmFuZCBhcyBpbml0aWFsIHZhbHVlcyBmb3IgTUNNQywgYnV0IHdlIG5vdyBzdG9wIHRoZSBvcHRpbWl6YXRpb24KZWFybHkuIEJ5IGFkZGluZyBgdG9sX29iaj0xMGAgYXJndW1lbnQsIHRoZSBvcHRpbWl6YXRpb24gc3RvcHMgd2hlbgp0aGUgY2hhbmdlIGluIHRoZSBsb2cgcG9zdGVyaW9yIGRlbnNpdHkgaXMgbGVzcyB0aGFuIDEwLCB3aGljaCBpcwpsaWtlbHkgdG8gaGFwcGVuZWQgYmVmb3JlIHJlYWNoaW5nIHRoZSBtb2RlLgoKYGBge3Igb3B0NCwgcmVzdWx0cz0naGlkZSd9Cm9wdDQgPC0gbW9kZWw0JG9wdGltaXplKGRhdGE9c3RhbmRhdGE0LCBpbml0PTAsIGFsZ29yaXRobT0nYmZncycsIHRvbF9vYmo9MTApCmBgYAoKQ2hlY2sgd2hldGhlciBwYXJhbWV0ZXJzIGhhdmUgcmVhc29uYWJsZSB2YWx1ZXMKCmBgYHtyIH0Kb2RyYXdzNCA8LSBvcHQ0JGRyYXdzKCkKc3Vic2V0KG9kcmF3czQsIHZhcmlhYmxlPWMoJ3NpZ21hXycsJ2xlbmd0aHNjYWxlXycsJ3NpZ21hJyksIHJlZ2V4PVRSVUUpCnN1YnNldChvZHJhd3M0LCB2YXJpYWJsZT1jKCdiZXRhX2YzJykpCmBgYAoKQ29tcGFyZSB0aGUgbW9kZWwgdG8gdGhlIGRhdGEKCmBgYHtyIH0KRWYgPC0gZXhwKGFzLm51bWVyaWMoc3Vic2V0KG9kcmF3czQsIHZhcmlhYmxlPSdmJykpKQpFZjEgPC0gYXMubnVtZXJpYyhzdWJzZXQob2RyYXdzNCwgdmFyaWFibGU9J2YxJykpCkVmMSA8LSBleHAoRWYxIC0gbWVhbihFZjEpICsgbWVhbihsb2coZGF0YSRiaXJ0aHNfcmVsYXRpdmUxMDApKSkKRWYyIDwtIGFzLm51bWVyaWMoc3Vic2V0KG9kcmF3czQsIHZhcmlhYmxlPSdmMicpKQpFZjIgPC0gZXhwKEVmMiAtIG1lYW4oRWYyKSArIG1lYW4obG9nKGRhdGEkYmlydGhzX3JlbGF0aXZlMTAwKSkpCkVmX2RheV9vZl93ZWVrIDwtIGFzLm51bWVyaWMoc3Vic2V0KG9kcmF3czQsIHZhcmlhYmxlPSdmX2RheV9vZl93ZWVrJykpCkVmX2RheV9vZl93ZWVrIDwtIGV4cChFZl9kYXlfb2Zfd2VlayAtIG1lYW4oRWZfZGF5X29mX3dlZWspICsgbWVhbihsb2coZGF0YSRiaXJ0aHNfcmVsYXRpdmUxMDApKSkKRWYzIDwtIGFzLm51bWVyaWMoc3Vic2V0KG9kcmF3czQsIHZhcmlhYmxlPSdmMycpKQpFZjMgPC0gZXhwKEVmMyAtIG1lYW4oRWYzKSArIG1lYW4obG9nKGRhdGEkYmlydGhzX3JlbGF0aXZlMTAwKSkpCnBmIDwtIGRhdGEgJT4lCiAgbXV0YXRlKEVmID0gRWYpICU+JQogIGdncGxvdChhZXMoeD1kYXRlLCB5PWJpcnRoc19yZWxhdGl2ZTEwMCkpICsgZ2VvbV9wb2ludChjb2xvcj1zZXQxWzJdLCBhbHBoYT0wLjIpICsKICBnZW9tX2xpbmUoYWVzKHk9RWYpLCBjb2xvcj1zZXQxWzFdLCBhbHBoYT0wLjc1KSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PTEwMCwgY29sb3I9J2dyYXknKSArCiAgbGFicyh4PSJEYXRlIiwgeT0iUmVsYXRpdmUgbnVtYmVyIG9mIGJpcnRocyIpCnBmMSA8LSBkYXRhICU+JQogIG11dGF0ZShFZjEgPSBFZjEpICU+JQogIGdncGxvdChhZXMoeD1kYXRlLCB5PWJpcnRoc19yZWxhdGl2ZTEwMCkpICsgZ2VvbV9wb2ludChjb2xvcj1zZXQxWzJdLCBhbHBoYT0wLjIpICsKICBnZW9tX2xpbmUoYWVzKHk9RWYxKSwgY29sb3I9c2V0MVsxXSkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdD0xMDAsIGNvbG9yPSdncmF5JykgKwogIGxhYnMoeD0iRGF0ZSIsIHk9IlJlbGF0aXZlIG51bWJlciBvZiBiaXJ0aHMiKQpwZjIgPC0gZGF0YSAlPiUKICBtdXRhdGUoRWYyID0gRWYyKSAlPiUKICBncm91cF9ieShkYXlfb2ZfeWVhcjIpICU+JQogIHN1bW1hcmlzZShtZWFuYmlydGhzPW1lYW4oYmlydGhzX3JlbGF0aXZlMTAwKSwgbWVhbkVmMj1tZWFuKEVmMikpICU+JQogIGdncGxvdChhZXMoeD1hcy5EYXRlKCIxOTg3LTEyLTMxIikrZGF5X29mX3llYXIyLCB5PW1lYW5iaXJ0aHMpKSArIGdlb21fcG9pbnQoY29sb3I9c2V0MVsyXSwgYWxwaGE9MC4yKSArCiAgc2NhbGVfeF9kYXRlKGRhdGVfYnJlYWtzID0gIjEgbW9udGgiLCBkYXRlX2xhYmVscyA9ICIlYiIpICsKICBnZW9tX2xpbmUoYWVzKHk9bWVhbkVmMiksIGNvbG9yPXNldDFbMV0pICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9MTAwLCBjb2xvcj0nZ3JheScpICsKICBsYWJzKHg9IkRhdGUiLCB5PSJSZWxhdGl2ZSBudW1iZXIgb2YgYmlydGhzIG9mIHllYXIiKQpwZjMgPC0gZ2dwbG90KGRhdGE9ZGF0YSwgYWVzKHg9ZGF5X29mX3dlZWssIHk9YmlydGhzX3JlbGF0aXZlMTAwKSkgKyBnZW9tX3BvaW50KGNvbG9yPXNldDFbMl0sIGFscGhhPTAuMikgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSAxOjcsIGxhYmVscz1jKCdNb24nLCdUdWUnLCdXZWQnLCdUaHUnLCdGcmknLCdTYXQnLCdTdW4nKSkgKwogIGdlb21fbGluZShkYXRhPWRhdGEuZnJhbWUoeD0xOjcseT1FZl9kYXlfb2Zfd2VlayksIGFlcyh4PXgsIHk9RWZfZGF5X29mX3dlZWspLCBjb2xvcj1zZXQxWzFdKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PTEwMCwgY29sb3I9J2dyYXknKSArCiAgbGFicyh4PSJEYXRlIiwgeT0iUmVsYXRpdmUgbnVtYmVyIG9mIGJpcnRocyBvZiB3ZWVrIikKcGYzYiA8LSBkYXRhICU+JQogIG11dGF0ZShFZjMgPSBFZjMpICU+JQogIGdncGxvdChhZXMoeD1kYXRlLCB5PWJpcnRoc19yZWxhdGl2ZTEwMC9FZjEvRWYyKjEwMCoxMDApKSArIGdlb21fcG9pbnQoY29sb3I9c2V0MVsyXSwgYWxwaGE9MC4yKSArCiAgZ2VvbV9wb2ludChhZXMoeT1FZjMpLCBjb2xvcj1zZXQxWzFdLCBzaXplPTAuMSkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdD0xMDAsIGNvbG9yPSdncmF5JykgKwogIGxhYnMoeD0iRGF0ZSIsIHk9IlJlbGF0aXZlIG51bWJlciBvZiBiaXJ0aHMiKQoocGYgKyBwZjEpIC8gKHBmMiArIHBmM2IpCmBgYAoKU2FtcGxlIHNob3J0IGNoYWlucyB1c2luZyB0aGUgZWFybHkgc3RvcHBlZCBvcHRpbWl6YXRpb24gcmVzdWx0IGFzCmluaXRpYWwgdmFsdWVzIChhbHRob3VnaCB0aGUgcmVzdWx0IGZyb20gc2hvcnQgY2hhaW5zIGNhbiBiZSB1c2VmdWwKaW4gYSBxdWljayB3b3JrZmxvdywgdGhlIHJlc3VsdCBzaG91bGQgbm90IGJlIHVzZWQgYXMgdGhlIGZpbmFsCnJlc3VsdCkuCgpgYGB7ciB9CmluaXQ0IDwtIHNhcHBseShjKCdsZW5ndGhzY2FsZV9mMScsJ2xlbmd0aHNjYWxlX2YyJywnbGVuZ3Roc2NhbGVfZzMnLAogICAgICAgICAgICAgICAgICAnc2lnbWFfZjEnLCdzaWdtYV9mMicsJ3NpZ21hX2czJywnc2lnbWEnLAogICAgICAgICAgICAgICAgICAnYmV0YV9mMScsJ2JldGFfZjInLCdiZXRhX2YzJywnYmV0YV9nMycpLAogICAgICAgICAgICAgICAgZnVuY3Rpb24odmFyaWFibGUpIHthcy5udW1lcmljKHN1YnNldChvZHJhd3M0LCB2YXJpYWJsZT12YXJpYWJsZSkpfSkKYGBgCmBgYHtyIGZpdDQsIHJlc3VsdHM9J2hpZGUnfQpmaXQ0IDwtIG1vZGVsNCRzYW1wbGUoZGF0YT1zdGFuZGF0YTQsIGl0ZXJfd2FybXVwPTEwMCwgaXRlcl9zYW1wbGluZz0xMDAsCiAgICAgICAgICAgICAgICAgICAgICBjaGFpbnM9NCwgcGFyYWxsZWxfY2hhaW5zPTQsCiAgICAgICAgICAgICAgICAgICAgICBpbml0PWZ1bmN0aW9uKCkgeyBpbml0NCB9LCByZWZyZXNoPTEwKQpgYGAKCkNoZWNrIHdoZXRoZXIgcGFyYW1ldGVycyBoYXZlIHJlYXNvbmFibGUgdmFsdWVzCgpgYGB7ciB9CmRyYXdzNCA8LSBmaXQ0JGRyYXdzKCkKc3VtbWFyaXNlX2RyYXdzKHN1YnNldChkcmF3czQsIHZhcmlhYmxlPWMoJ3NpZ21hXycsJ2xlbmd0aHNjYWxlXycsJ3NpZ21hJyksIHJlZ2V4PVRSVUUpKQpzdW1tYXJpc2VfZHJhd3Moc3Vic2V0KGRyYXdzNCwgdmFyaWFibGU9YygnYmV0YV9mMycpKSkKYGBgCgpDb21wYXJlIHRoZSBtb2RlbCB0byB0aGUgZGF0YQoKYGBge3IgfQpkcmF3czQgPC0gYXNfZHJhd3NfbWF0cml4KGRyYXdzNCkKRWYgPC0gZXhwKGFwcGx5KHN1YnNldChkcmF3czQsIHZhcmlhYmxlPSdmJyksIDIsIG1lZGlhbikpCkVmMSA8LSBhcHBseShzdWJzZXQoZHJhd3M0LCB2YXJpYWJsZT0nZjEnKSwgMiwgbWVkaWFuKQpFZjEgPC0gZXhwKEVmMSAtIG1lYW4oRWYxKSArIG1lYW4obG9nKGRhdGEkYmlydGhzX3JlbGF0aXZlMTAwKSkpCkVmMiA8LSBhcHBseShzdWJzZXQoZHJhd3M0LCB2YXJpYWJsZT0nZjInKSwgMiwgbWVkaWFuKQpFZjIgPC0gZXhwKEVmMiAtIG1lYW4oRWYyKSArIG1lYW4obG9nKGRhdGEkYmlydGhzX3JlbGF0aXZlMTAwKSkpCkVmX2RheV9vZl93ZWVrIDwtIGFwcGx5KHN1YnNldChkcmF3czQsIHZhcmlhYmxlPSdmX2RheV9vZl93ZWVrJyksIDIsIG1lZGlhbikKRWZfZGF5X29mX3dlZWsgPC0gZXhwKEVmX2RheV9vZl93ZWVrIC0gbWVhbihFZl9kYXlfb2Zfd2VlaykgKyBtZWFuKGxvZyhkYXRhJGJpcnRoc19yZWxhdGl2ZTEwMCkpKQpFZjMgPC0gYXBwbHkoc3Vic2V0KGRyYXdzNCwgdmFyaWFibGU9J2YzJyksIDIsIG1lZGlhbikKRWYzIDwtIGV4cChFZjMgLSBtZWFuKEVmMykgKyBtZWFuKGxvZyhkYXRhJGJpcnRoc19yZWxhdGl2ZTEwMCkpKQpwZiA8LSBkYXRhICU+JQogIG11dGF0ZShFZiA9IEVmKSAlPiUKICBnZ3Bsb3QoYWVzKHg9ZGF0ZSwgeT1iaXJ0aHNfcmVsYXRpdmUxMDApKSArIGdlb21fcG9pbnQoY29sb3I9c2V0MVsyXSwgYWxwaGE9MC4yKSArCiAgZ2VvbV9saW5lKGFlcyh5PUVmKSwgY29sb3I9c2V0MVsxXSwgYWxwaGE9MC43NSkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdD0xMDAsIGNvbG9yPSdncmF5JykgKwogIGxhYnMoeD0iRGF0ZSIsIHk9IlJlbGF0aXZlIG51bWJlciBvZiBiaXJ0aHMiKQpwZjEgPC0gZGF0YSAlPiUKICBtdXRhdGUoRWYxID0gRWYxKSAlPiUKICBnZ3Bsb3QoYWVzKHg9ZGF0ZSwgeT1iaXJ0aHNfcmVsYXRpdmUxMDApKSArIGdlb21fcG9pbnQoY29sb3I9c2V0MVsyXSwgYWxwaGE9MC4yKSArCiAgZ2VvbV9saW5lKGFlcyh5PUVmMSksIGNvbG9yPXNldDFbMV0pICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9MTAwLCBjb2xvcj0nZ3JheScpICsKICBsYWJzKHg9IkRhdGUiLCB5PSJSZWxhdGl2ZSBudW1iZXIgb2YgYmlydGhzIikKcGYyIDwtIGRhdGEgJT4lCiAgbXV0YXRlKEVmMiA9IEVmMikgJT4lCiAgZ3JvdXBfYnkoZGF5X29mX3llYXIyKSAlPiUKICBzdW1tYXJpc2UobWVhbmJpcnRocz1tZWFuKGJpcnRoc19yZWxhdGl2ZTEwMCksIG1lYW5FZjI9bWVhbihFZjIpKSAlPiUKICBnZ3Bsb3QoYWVzKHg9YXMuRGF0ZSgiMTk4Ny0xMi0zMSIpK2RheV9vZl95ZWFyMiwgeT1tZWFuYmlydGhzKSkgKyBnZW9tX3BvaW50KGNvbG9yPXNldDFbMl0sIGFscGhhPTAuMikgKwogIHNjYWxlX3hfZGF0ZShkYXRlX2JyZWFrcyA9ICIxIG1vbnRoIiwgZGF0ZV9sYWJlbHMgPSAiJWIiKSArCiAgZ2VvbV9saW5lKGFlcyh5PW1lYW5FZjIpLCBjb2xvcj1zZXQxWzFdKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PTEwMCwgY29sb3I9J2dyYXknKSArCiAgbGFicyh4PSJEYXRlIiwgeT0iUmVsYXRpdmUgbnVtYmVyIG9mIGJpcnRocyBvZiB5ZWFyIikKcGYzIDwtIGdncGxvdChkYXRhPWRhdGEsIGFlcyh4PWRheV9vZl93ZWVrLCB5PWJpcnRoc19yZWxhdGl2ZTEwMCkpICsgZ2VvbV9wb2ludChjb2xvcj1zZXQxWzJdLCBhbHBoYT0wLjIpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gMTo3LCBsYWJlbHM9YygnTW9uJywnVHVlJywnV2VkJywnVGh1JywnRnJpJywnU2F0JywnU3VuJykpICsKICBnZW9tX2xpbmUoZGF0YT1kYXRhLmZyYW1lKHg9MTo3LHk9RWZfZGF5X29mX3dlZWspLCBhZXMoeD14LCB5PUVmX2RheV9vZl93ZWVrKSwgY29sb3I9c2V0MVsxXSkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdD0xMDAsIGNvbG9yPSdncmF5JykgKwogIGxhYnMoeD0iRGF0ZSIsIHk9IlJlbGF0aXZlIG51bWJlciBvZiBiaXJ0aHMgb2Ygd2VlayIpCnBmM2IgPC0gZGF0YSAlPiUKICBtdXRhdGUoRWYzID0gRWYzKSAlPiUKICBnZ3Bsb3QoYWVzKHg9ZGF0ZSwgeT1iaXJ0aHNfcmVsYXRpdmUxMDAvRWYxL0VmMioxMDAqMTAwKSkgKyBnZW9tX3BvaW50KGNvbG9yPXNldDFbMl0sIGFscGhhPTAuMikgKwogIGdlb21fcG9pbnQoYWVzKHk9RWYzKSwgY29sb3I9c2V0MVsxXSwgc2l6ZT0wLjEpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9MTAwLCBjb2xvcj0nZ3JheScpICsKICBsYWJzKHg9IkRhdGUiLCB5PSJSZWxhdGl2ZSBudW1iZXIgb2YgYmlydGhzIikKKHBmICsgcGYxKSAvIChwZjIgKyBwZjNiKQpgYGAKClRoZSBtb2RlbCBmaXRzIHdlbGwgdGhlIGRpZmZlcmVudCBicmFuY2hlcyB2aXNpYmxlIGluIHBsb3R0ZWQgZGFpbHkKcmVsYXRpdmUgbnVtYmVyIG9mIGJpcnRocywgdGhhdCBpcywgaXQgaXMgYWJsZSB0byBtb2RlbCB0aGUKaW5jcmVhc2luZyB3ZWVrZW5kIGVmZmVjdC4KCiMjIyBNb2RlbCA1OiBsb25nIHRlcm0gc21vb3RoICsgc2Vhc29uYWwgKyB3ZWVrZGF5IHdpdGggdGltZSBkZXBlbmRlbnQgbWFnbml0dWRlICsgZGF5IG9mIHllYXIgUkhTCgpUaGUgbmV4dCBjb21wb25lbnQgdG8gYWRkIGlzIGRheSBvZiB5ZWFyIGVmZmVjdC4gTWFueSBiYW5rIGhvbGlkYXlzCmFyZSBldmVyeSB5ZWFyIG9uIHRoZSBzYW1lIGRheSBvZiB5ZWFyIGFuZCB0aGVyZSBtaWdodCBiZSBhbHNvCm90aGVyIHNwZWNpYWwgZGF5cyB0aGF0IGFyZSBmYXZvcmVkIG9yIGRpc2Zhdm9yZWQuCgokJApmID0gXG1ib3h7aW50ZXJjZXB0fSArIGZfMSArIGZfMiArIFxleHAoZ18zKVxiZXRhX3tcbWJveHtkYXkgb2Ygd2Vla319ICsgXGJldGFfe1xtYm94e2RheSBvZiB5ZWFyfX1cXApcbWJveHtpbnRlcmNlcHR9IFxzaW0gXG1ib3h7bm9ybWFsfSgwLDEpXFwKZl8xIFxzaW0gXG1ib3h7R1B9KDAsS18xKVxcCmZfMiBcc2ltIFxtYm94e0dQfSgwLEtfMilcXApnXzMgXHNpbSBcbWJveHtHUH0oMCxLXzMpXFwKXGJldGFfe1xtYm94e2RheSBvZiB3ZWVrfX0gPSAwIFxxdWFkIFxtYm94e2lmIGRheSBvZiB3ZWVrIGlzIE1vbmRheX1cXApcYmV0YV97XG1ib3h7ZGF5IG9mIHdlZWt9fSBcc2ltIFxtYm94e25vcm1hbH0oMCwxKSBccXVhZCBcbWJveHtpZiBkYXkgb2Ygd2VlayBpcyBub3QgTW9uZGF5fVxcClxiZXRhX3tcbWJveHtkYXkgb2YgeWVhcn19IFxzaW0gUkhTKDAsMC4xKQokJApBcyB3ZSBhc3N1bWUgdGhhdCBvbmx5IHNvbWUgZGF5cyBvZiB5ZWFyIGFyZSBzcGVjaWFsLCB3ZSB1c2UKcmVndWxhcml6ZWQgaG9yc2VzaG9lIChSSFMpIHByaW9yIGZvciBkYXkgb2YgeWVhciBlZmZlY3RzLgoKQXQgdGhpcyBwb2ludCB0aGUgb3B0aW1pemF0aW9uIGRpZG4ndCBwcm9kdWNlIHJlYXNvbmFibGUgcmVzdWx0IGFzCmVhcmxpZXIgYW5kIHNhbXBsaW5nIHR1cm5lZCBvdXQgdG8gYmUgdmVyeSBzbG93LiBXZSBhc3N1bWVkIHRoZQpvcHRpbWl6YXRpb24gZmFpbHMgYmVjYXVzZSB0aGVyZSB3ZXJlIHNvIG1hbnkgbW9yZSBwYXJhbWV0ZXJzIHdpdGgKaGllcmFyY2hpY2FsIHByaW9yLiBBcyBldmVuIHRoZSBzaG9ydCBjaGFpbiBzYW1wbGluZyB3b3VsZCBoYXZlCnRha2VuIG1vcmUgdGhhbiBob3VyLCBpdCB3b3VsZCBoYXZlIGJlZW4gdGltZSBjb25zdW1pbmcgdG8gZnVydGhlcgp0byB0ZXN0IHRoZSBtb2RlbC4gQXMgcGFydCBvZiB0aGUgcXVpY2sgaXRlcmF0aXZlIG1vZGVsIGJ1aWxkaW5nIGl0CndhcyBiZXR0ZXIgdG8gZ2l2ZSB1cCBvbiB0aGlzIG1vZGVsIGZvciBhIG1vbWVudC4KCkNvbXBpbGUgU3RhbiBtb2RlbCA1IFtncGJmNS5zdGFuXShodHRwczovL2dpdGh1Yi5jb20vYXZlaHRhcmkvY2FzZXN0dWRpZXMvYmxvYi9tYXN0ZXIvQmlydGhkYXlzL2dwYmY1LnN0YW4pCgpgYGB7ciBtb2RlbDUsIHJlc3VsdHM9J2hpZGUnfQptb2RlbDUgPC0gY21kc3Rhbl9tb2RlbChzdGFuX2ZpbGUgPSByb290KCJCaXJ0aGRheXMiLCAiZ3BiZjUuc3RhbiIpLAogICAgICAgICAgICAgICAgICAgICAgICBpbmNsdWRlX3BhdGhzID0gcm9vdCgiQmlydGhkYXlzIikpCmBgYAoKRGF0YSB0byBiZSBwYXNzZWQgdG8gU3RhbgoKYGBge3IgfQpzdGFuZGF0YTUgPC0gbGlzdCh4PWRhdGEkaWQsCiAgICAgICAgICAgICAgICAgIHk9bG9nKGRhdGEkYmlydGhzX3JlbGF0aXZlMTAwKSwKICAgICAgICAgICAgICAgICAgTj1sZW5ndGgoZGF0YSRpZCksCiAgICAgICAgICAgICAgICAgIGNfZjE9MS41LCAjIGZhY3RvciBjIG9mIGJhc2lzIGZ1bmN0aW9ucyBmb3IgR1AgZm9yIGYxCiAgICAgICAgICAgICAgICAgIE1fZjE9MjAsICAjIG51bWJlciBvZiBiYXNpcyBmdW5jdGlvbnMgZm9yIEdQIGZvciBmMQogICAgICAgICAgICAgICAgICBKX2YyPTIwLCAgIyBudW1iZXIgb2YgYmFzaXMgZnVuY3Rpb25zIGZvciBwZXJpb2RpYyBmMgogICAgICAgICAgICAgICAgICBjX2czPTEuNSwgIyBmYWN0b3IgYyBvZiBiYXNpcyBmdW5jdGlvbnMgZm9yIEdQIGZvciBnMwogICAgICAgICAgICAgICAgICBNX2czPTUsICAgIyBudW1iZXIgb2YgYmFzaXMgZnVuY3Rpb25zIGZvciBHUCBmb3IgZzMKICAgICAgICAgICAgICAgICAgc2NhbGVfZ2xvYmFsPTAuMSwgIyBnbG92YWwgc2NhbGUgZm9yIFJIUyBwcmlvcgogICAgICAgICAgICAgICAgICBkYXlfb2Zfd2Vlaz1kYXRhJGRheV9vZl93ZWVrLAogICAgICAgICAgICAgICAgICBkYXlfb2ZfeWVhcj1kYXRhJGRheV9vZl95ZWFyMikgIyAxc3QgTWFyY2ggPSA2MSBldmVyeSB5ZWFyCmBgYAoKT3B0aW1pemluZyBpcyBmYXN0ZXIgdGhhbiBzYW1wbGluZyAoYWx0aG91Z2ggdGhpcyByZXN1bHQgY2FuIGJlCnVzZWZ1bCBpbiBhIHF1aWNrIHdvcmtmbG93LCB0aGUgcmVzdWx0IHNob3VsZCBub3QgYmUgdXNlZCBhcyB0aGUKZmluYWwgcmVzdWx0KS4KCmBgYHtyIG9wdDUsIHJlc3VsdHM9J2hpZGUnfQpvcHQ1IDwtIG1vZGVsNSRvcHRpbWl6ZShkYXRhPXN0YW5kYXRhNSwgaW5pdD0wLCBhbGdvcml0aG09J2xiZmdzJywKICAgICAgICAgICAgICAgICAgICAgICAgaGlzdG9yeT0xMDAsIHRvbF9vYmo9MTApCmBgYAoKQ2hlY2sgd2hldGhlciBwYXJhbWV0ZXJzIGhhdmUgcmVhc29uYWJsZSB2YWx1ZXMKCmBgYHtyIH0Kb2RyYXdzNSA8LSBvcHQ1JGRyYXdzKCkKc3Vic2V0KG9kcmF3czUsIHZhcmlhYmxlPWMoJ3NpZ21hXycsJ2xlbmd0aHNjYWxlXycsJ3NpZ21hJyksIHJlZ2V4PVRSVUUpCnN1YnNldChvZHJhd3M1LCB2YXJpYWJsZT1jKCdiZXRhX2YzJykpCkVmNCA8LSBhcy5udW1lcmljKHN1YnNldChvZHJhd3M1LCB2YXJpYWJsZT0nYmV0YV9mNCcpKSpzZChsb2coZGF0YSRiaXJ0aHNfcmVsYXRpdmUxMDApKQpFZjQgPC0gZXhwKEVmNCkqMTAwCmRhdGEuZnJhbWUoeD1hcy5EYXRlKCIxOTg4LTAxLTAxIikrMDozNjUsIHk9RWY0KSAlPiUKICBnZ3Bsb3QoYWVzKHg9eCx5PXkpKSArIGdlb21fbGluZShjb2xvcj1zZXQxWzFdKSArCiAgc2NhbGVfeF9kYXRlKGRhdGVfYnJlYWtzID0gIjEgbW9udGgiLCBkYXRlX2xhYmVscyA9ICIlYiIpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9MTAwLCBjb2xvcj0nZ3JheScpICsKICBsYWJzKHg9IkRhdGUiLCB5PSJSZWxhdGl2ZSBudW1iZXIgb2YgYmlydGhzIG9mIHllYXIiKQpgYGAKCkNvbXBhcmUgdGhlIG1vZGVsIHRvIHRoZSBkYXRhCgpgYGB7ciB9CkVmIDwtIGV4cChhcy5udW1lcmljKHN1YnNldChvZHJhd3M1LCB2YXJpYWJsZT0nZicpKSkKRWYxIDwtIGFzLm51bWVyaWMoc3Vic2V0KG9kcmF3czUsIHZhcmlhYmxlPSdmMScpKQpFZjEgPC0gZXhwKEVmMSAtIG1lYW4oRWYxKSArIG1lYW4obG9nKGRhdGEkYmlydGhzX3JlbGF0aXZlMTAwKSkpCkVmMiA8LSBhcy5udW1lcmljKHN1YnNldChvZHJhd3M1LCB2YXJpYWJsZT0nZjInKSkKRWYyIDwtIGV4cChFZjIgLSBtZWFuKEVmMikgKyBtZWFuKGxvZyhkYXRhJGJpcnRoc19yZWxhdGl2ZTEwMCkpKQpFZl9kYXlfb2Zfd2VlayA8LSBhcy5udW1lcmljKHN1YnNldChvZHJhd3M1LCB2YXJpYWJsZT0nZl9kYXlfb2Zfd2VlaycpKQpFZl9kYXlfb2Zfd2VlayA8LSBleHAoRWZfZGF5X29mX3dlZWsgLSBtZWFuKEVmX2RheV9vZl93ZWVrKSArIG1lYW4obG9nKGRhdGEkYmlydGhzX3JlbGF0aXZlMTAwKSkpCkVmNCA8LSBhcy5udW1lcmljKHN1YnNldChvZHJhd3M1LCB2YXJpYWJsZT0nYmV0YV9mNCcpKSpzZChsb2coZGF0YSRiaXJ0aHNfcmVsYXRpdmUxMDApKQpFZjQgPC0gZXhwKEVmNCkqMTAwCnBmIDwtIGRhdGEgJT4lCiAgbXV0YXRlKEVmID0gRWYpICU+JQogIGdncGxvdChhZXMoeD1kYXRlLCB5PWJpcnRoc19yZWxhdGl2ZTEwMCkpICsgZ2VvbV9wb2ludChjb2xvcj1zZXQxWzJdLCBhbHBoYT0wLjIpICsKICBnZW9tX2xpbmUoYWVzKHk9RWYpLCBjb2xvcj1zZXQxWzFdLCBhbHBoYT0wLjc1KSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PTEwMCwgY29sb3I9J2dyYXknKSArCiAgbGFicyh4PSJEYXRlIiwgeT0iUmVsYXRpdmUgbnVtYmVyIG9mIGJpcnRocyIpCnBmMSA8LSBkYXRhICU+JQogIG11dGF0ZShFZjEgPSBFZjEpICU+JQogIGdncGxvdChhZXMoeD1kYXRlLCB5PWJpcnRoc19yZWxhdGl2ZTEwMCkpICsgZ2VvbV9wb2ludChjb2xvcj1zZXQxWzJdLCBhbHBoYT0wLjIpICsKICBnZW9tX2xpbmUoYWVzKHk9RWYxKSwgY29sb3I9c2V0MVsxXSkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdD0xMDAsIGNvbG9yPSdncmF5JykgKwogIGxhYnMoeD0iRGF0ZSIsIHk9IlJlbGF0aXZlIG51bWJlciBvZiBiaXJ0aHMiKQpwZjIgPC0gZGF0YSAlPiUKICBtdXRhdGUoRWYyID0gRWYyKSAlPiUKICBncm91cF9ieShkYXlfb2ZfeWVhcjIpICU+JQogIHN1bW1hcmlzZShtZWFuYmlydGhzPW1lYW4oYmlydGhzX3JlbGF0aXZlMTAwKSwgbWVhbkVmMj1tZWFuKEVmMikpICU+JQogIGdncGxvdChhZXMoeD1hcy5EYXRlKCIxOTg3LTEyLTMxIikrZGF5X29mX3llYXIyLCB5PW1lYW5iaXJ0aHMpKSArIGdlb21fcG9pbnQoY29sb3I9c2V0MVsyXSwgYWxwaGE9MC4yKSArCiAgc2NhbGVfeF9kYXRlKGRhdGVfYnJlYWtzID0gIjEgbW9udGgiLCBkYXRlX2xhYmVscyA9ICIlYiIpICsKICBnZW9tX2xpbmUoYWVzKHk9bWVhbkVmMiksIGNvbG9yPXNldDFbMV0pICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9MTAwLCBjb2xvcj0nZ3JheScpICsKICBsYWJzKHg9IkRhdGUiLCB5PSJSZWxhdGl2ZSBudW1iZXIgb2YgYmlydGhzIG9mIHllYXIiKQpwZjMgPC0gZ2dwbG90KGRhdGE9ZGF0YSwgYWVzKHg9ZGF5X29mX3dlZWssIHk9YmlydGhzX3JlbGF0aXZlMTAwKSkgKyBnZW9tX3BvaW50KGNvbG9yPXNldDFbMl0sIGFscGhhPTAuMikgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSAxOjcsIGxhYmVscz1jKCdNb24nLCdUdWUnLCdXZWQnLCdUaHUnLCdGcmknLCdTYXQnLCdTdW4nKSkgKwogIGdlb21fbGluZShkYXRhPWRhdGEuZnJhbWUoeD0xOjcseT1FZl9kYXlfb2Zfd2VlayksIGFlcyh4PXgsIHk9RWZfZGF5X29mX3dlZWspLCBjb2xvcj1zZXQxWzFdKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PTEwMCwgY29sb3I9J2dyYXknKSArCiAgbGFicyh4PSJEYXRlIiwgeT0iUmVsYXRpdmUgbnVtYmVyIG9mIGJpcnRocyBvZiB3ZWVrIikKcGYyYiA8LWRhdGEuZnJhbWUoeD1hcy5EYXRlKCIxOTU5LTEyLTMxIikrMTozNjYsIHk9RWY0KSAlPiUKICBnZ3Bsb3QoYWVzKHg9eCx5PXkpKSArIGdlb21fbGluZShjb2xvcj1zZXQxWzFdKSArCiAgc2NhbGVfeF9kYXRlKGRhdGVfYnJlYWtzID0gIjEgbW9udGgiLCBkYXRlX2xhYmVscyA9ICIlYiIpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9MTAwLCBjb2xvcj0nZ3JheScpICsKICBsYWJzKHg9IkRhdGUiLCB5PSJSZWxhdGl2ZSBudW1iZXIgb2YgYmlydGhzIG9mIHllYXIiKQoocGYgKyBwZjEpIC8gKHBmMiArIHBmMykgLyAocGYyYikKYGBgCgpUaGUgcXVpY2sgbW9kZWwgZml0IGZvciBtb2RlbCA1IGlzIG5vdCBnb29kLCBidXQgYXMgdGhlIHNhbXBsaW5nCndhcyB2ZXJ5IHNsb3cgaXQgd2Fzbid0IGVhc3kgdG8gZmlndXJlIG91dCB3aGF0IGlzIGdvaW5nIHdyb25nLgoKIyMjIE1vZGVsIDY6IGxvbmcgdGVybSBzbW9vdGggKyBzZWFzb25hbCArIHdlZWtkYXkgKyBkYXkgb2YgeWVhcgoKVG8gc2ltcGxpZnkgdGhlIGFuYWx5c2lzIG9mIHRoZSBkYXkgb2YgeWVhciBlZmZlY3QgYW5kIG1ha2UgdGhlCmluZmVyZW5jZSBkdXJpbmcgdGhlIGV4cGxvcmF0aW9uIGZhc3Rlciwgd2UgZHJvcCB0aGUgdGltZSBkZXBlbmRlbnQKZGF5IG9mIHdlZWsgZWZmZWN0IGFuZCBSSFMgZm9yIGEgbW9tZW50IGFuZCB1c2Ugbm9ybWFsIHByaW9yIGZvcgp0aGUgZGF5IG9mIHllYXIgZWZmZWN0LgoKJCQKZiA9IFxtYm94e2ludGVyY2VwdH0gKyBmXzEgKyBmXzIgKyBcYmV0YV97XG1ib3h7ZGF5IG9mIHdlZWt9fSArIFxiZXRhX3tcbWJveHtkYXkgb2YgeWVhcn19XFwKXG1ib3h7aW50ZXJjZXB0fSBcc2ltIFxtYm94e25vcm1hbH0oMCwxKVxcCmZfMSBcc2ltIFxtYm94e0dQfSgwLEtfMSlcXApmXzIgXHNpbSBcbWJveHtHUH0oMCxLXzIpXFwKXGJldGFfe1xtYm94e2RheSBvZiB3ZWVrfX0gPSAwIFxxdWFkIFxtYm94e2lmIGRheSBvZiB3ZWVrIGlzIE1vbmRheX1cXApcYmV0YV97XG1ib3h7ZGF5IG9mIHdlZWt9fSBcc2ltIFxtYm94e25vcm1hbH0oMCwxKSBccXVhZCBcbWJveHtpZiBkYXkgb2Ygd2VlayBpcyBub3QgTW9uZGF5fVxcClxiZXRhX3tcbWJveHtkYXkgb2YgeWVhcn19IFxzaW0gXG1ib3h7bm9ybWFsfSgwLDAuMSkKJCQKCkNvbXBpbGUgU3RhbiBtb2RlbCA2IFtncGJmNi5zdGFuXShodHRwczovL2dpdGh1Yi5jb20vYXZlaHRhcmkvY2FzZXN0dWRpZXMvYmxvYi9tYXN0ZXIvQmlydGhkYXlzL2dwYmY2LnN0YW4pCgpgYGB7ciBtb2RlbDYsIHJlc3VsdHM9J2hpZGUnfQptb2RlbDYgPC0gY21kc3Rhbl9tb2RlbChzdGFuX2ZpbGUgPSByb290KCJCaXJ0aGRheXMiLCAiZ3BiZjYuc3RhbiIpLAogICAgICAgICAgICAgICAgICAgICAgICBpbmNsdWRlX3BhdGhzID0gcm9vdCgiQmlydGhkYXlzIikpCmBgYAoKRGF0YSB0byBiZSBwYXNzZWQgdG8gU3RhbgoKYGBge3IgfQpzdGFuZGF0YTYgPC0gbGlzdCh4PWRhdGEkaWQsCiAgICAgICAgICAgICAgICAgIHk9bG9nKGRhdGEkYmlydGhzX3JlbGF0aXZlMTAwKSwKICAgICAgICAgICAgICAgICAgTj1sZW5ndGgoZGF0YSRpZCksCiAgICAgICAgICAgICAgICAgIGNfZjE9MS41LCAjIGZhY3RvciBjIG9mIGJhc2lzIGZ1bmN0aW9ucyBmb3IgR1AgZm9yIGYxCiAgICAgICAgICAgICAgICAgIE1fZjE9MjAsICMgbnVtYmVyIG9mIGJhc2lzIGZ1bmN0aW9ucyBmb3IgR1AgZm9yIGYxCiAgICAgICAgICAgICAgICAgIEpfZjI9MjAsICMgbnVtYmVyIG9mIGJhc2lzIGZ1bmN0aW9ucyBmb3IgcGVyaW9kaWMgZjIKICAgICAgICAgICAgICAgICAgZGF5X29mX3dlZWs9ZGF0YSRkYXlfb2Zfd2VlaywKICAgICAgICAgICAgICAgICAgZGF5X29mX3llYXI9ZGF0YSRkYXlfb2ZfeWVhcjIpICMgMXN0IE1hcmNoID0gNjEgZXZlcnkgeWVhcgpgYGAKCk9wdGltaXppbmcgaXMgZmFzdGVyIHRoYW4gc2FtcGxpbmcgKGFsdGhvdWdoIHRoaXMgcmVzdWx0IGNhbiBiZQp1c2VmdWwgaW4gYSBxdWljayB3b3JrZmxvdywgdGhlIHJlc3VsdCBzaG91bGQgbm90IGJlIHVzZWQgYXMgdGhlCmZpbmFsIHJlc3VsdCkuCgpgYGB7ciBvcHQ2LCByZXN1bHRzPSdoaWRlJ30Kb3B0NiA8LSBtb2RlbDYkb3B0aW1pemUoZGF0YT1zdGFuZGF0YTYsIGluaXQ9MCwgYWxnb3JpdGhtPSdsYmZncycsCiAgICAgICAgICAgICAgICAgICAgICAgIGhpc3Rvcnk9MTAwLCB0b2xfb2JqPTEwKQpgYGAKCkNoZWNrIHdoZXRoZXIgcGFyYW1ldGVycyBoYXZlIHJlYXNvbmFibGUgdmFsdWVzCgpgYGB7ciB9Cm9kcmF3czYgPC0gb3B0NiRkcmF3cygpCnN1YnNldChvZHJhd3M2LCB2YXJpYWJsZT1jKCdzaWdtYV8nLCdsZW5ndGhzY2FsZV8nLCdzaWdtYScpLCByZWdleD1UUlVFKQpzdWJzZXQob2RyYXdzNiwgdmFyaWFibGU9YygnYmV0YV9mMycpKQpFZjQgPC0gYXMubnVtZXJpYyhzdWJzZXQob2RyYXdzNiwgdmFyaWFibGU9J2JldGFfZjQnKSkqc2QobG9nKGRhdGEkYmlydGhzX3JlbGF0aXZlMTAwKSkKRWY0IDwtIGV4cChFZjQpKjEwMApkYXRhLmZyYW1lKHg9YXMuRGF0ZSgiMTk4OC0wMS0wMSIpKzA6MzY1LCB5PUVmNCkgJT4lCiAgZ2dwbG90KGFlcyh4PXgseT15KSkgKyBnZW9tX2xpbmUoY29sb3I9c2V0MVsxXSkgKwogIHNjYWxlX3hfZGF0ZShkYXRlX2JyZWFrcyA9ICIxIG1vbnRoIiwgZGF0ZV9sYWJlbHMgPSAiJWIiKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PTEwMCwgY29sb3I9J2dyYXknKSArCiAgbGFicyh4PSJEYXRlIiwgeT0iUmVsYXRpdmUgbnVtYmVyIG9mIGJpcnRocyBvZiB5ZWFyIikKYGBgCgpXZSByZWNvZ25pemUgc29tZSBmYW1pbGlhciBzdHJ1Y3R1cmUgaW4gdGhlIGRheSBvZiB5ZWFyIGVmZmVjdCBhbmQKcHJvY2VlZCB0byBzYW1wbGluZy4KU2FtcGxlIHNob3J0IGNoYWlucyB1c2luZyB0aGUgZWFybHkgc3RvcHBlZCBvcHRpbWl6YXRpb24gcmVzdWx0IGFzCmluaXRpYWwgdmFsdWVzIChhbHRob3VnaCB0aGUgcmVzdWx0IGZyb20gc2hvcnQgY2hhaW5zIGNhbiBiZSB1c2VmdWwKaW4gYSBxdWljayB3b3JrZmxvdywgdGhlIHJlc3VsdCBzaG91bGQgbm90IGJlIHVzZWQgYXMgdGhlIGZpbmFsCnJlc3VsdCkuCgpgYGB7ciB9CmluaXQ2IDwtIHNhcHBseShjKCdsZW5ndGhzY2FsZV9mMScsJ2xlbmd0aHNjYWxlX2YyJywKICAgICAgICAgICAgICAgICAgJ3NpZ21hX2YxJywnc2lnbWFfZjInLCdzaWdtYV9mNCcsJ3NpZ21hJywKICAgICAgICAgICAgICAgICAgJ2JldGFfZjEnLCdiZXRhX2YyJywnYmV0YV9mMycsJ2JldGFfZjQnKSwKICAgICAgICAgICAgICAgIGZ1bmN0aW9uKHZhcmlhYmxlKSB7YXMubnVtZXJpYyhzdWJzZXQob2RyYXdzNiwgdmFyaWFibGU9dmFyaWFibGUpKX0pCmBgYApgYGB7ciBmaXQ2LCByZXN1bHRzPSdoaWRlJ30KZml0NiA8LSBtb2RlbDYkc2FtcGxlKGRhdGE9c3RhbmRhdGE2LCBpdGVyX3dhcm11cD0xMDAsIGl0ZXJfc2FtcGxpbmc9MTAwLAogICAgICAgICAgICAgICAgICAgICAgY2hhaW5zPTQsIHBhcmFsbGVsX2NoYWlucz00LAogICAgICAgICAgICAgICAgICAgICAgaW5pdD1mdW5jdGlvbigpIHsgaW5pdDYgfSkKYGBgCgpDaGVjayB3aGV0aGVyIHBhcmFtZXRlcnMgaGF2ZSByZWFzb25hYmxlIHZhbHVlcwoKYGBge3IgfQpkcmF3czYgPC0gZml0NiRkcmF3cygpCnN1bW1hcmlzZV9kcmF3cyhzdWJzZXQoZHJhd3M2LCB2YXJpYWJsZT1jKCdzaWdtYV8nLCdsZW5ndGhzY2FsZV8nLCdzaWdtYScpLCByZWdleD1UUlVFKSkKc3VtbWFyaXNlX2RyYXdzKHN1YnNldChkcmF3czYsIHZhcmlhYmxlPWMoJ2JldGFfZjMnKSkpCmRyYXdzNiA8LSBhc19kcmF3c19tYXRyaXgoZHJhd3M2KQpFZjQgPC0gYXBwbHkoc3Vic2V0KGRyYXdzNiwgdmFyaWFibGU9J2JldGFfZjQnKSwgMiwgbWVkaWFuKSpzZChsb2coZGF0YSRiaXJ0aHNfcmVsYXRpdmUxMDApKQpFZjQgPC0gZXhwKEVmNCkqMTAwCmRhdGEuZnJhbWUoeD1hcy5EYXRlKCIxOTg4LTAxLTAxIikrMDozNjUsIHk9RWY0KSAlPiUKICBnZ3Bsb3QoYWVzKHg9eCx5PXkpKSArIGdlb21fbGluZShjb2xvcj1zZXQxWzFdKSArCiAgc2NhbGVfeF9kYXRlKGRhdGVfYnJlYWtzID0gIjEgbW9udGgiLCBkYXRlX2xhYmVscyA9ICIlYiIpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9MTAwLCBjb2xvcj0nZ3JheScpICsKICBsYWJzKHg9IkRhdGUiLCB5PSJSZWxhdGl2ZSBudW1iZXIgb2YgYmlydGhzIG9mIHllYXIiKQpgYGAKCkNvbXBhcmUgdGhlIG1vZGVsIHRvIHRoZSBkYXRhCgpgYGB7ciB9CmRyYXdzNiA8LSBhc19kcmF3c19tYXRyaXgoZHJhd3M2KQpFZiA8LSBleHAoYXBwbHkoc3Vic2V0KGRyYXdzNiwgdmFyaWFibGU9J2YnKSwgMiwgbWVkaWFuKSkKRWYxIDwtIGFwcGx5KHN1YnNldChkcmF3czYsIHZhcmlhYmxlPSdmMScpLCAyLCBtZWRpYW4pCkVmMSA8LSBleHAoRWYxIC0gbWVhbihFZjEpICsgbWVhbihsb2coZGF0YSRiaXJ0aHNfcmVsYXRpdmUxMDApKSkKRWYyIDwtIGFwcGx5KHN1YnNldChkcmF3czYsIHZhcmlhYmxlPSdmMicpLCAyLCBtZWRpYW4pCkVmMiA8LSBleHAoRWYyIC0gbWVhbihFZjIpICsgbWVhbihsb2coZGF0YSRiaXJ0aHNfcmVsYXRpdmUxMDApKSkKRWZfZGF5X29mX3dlZWsgPC0gYXBwbHkoc3Vic2V0KGRyYXdzNiwgdmFyaWFibGU9J2ZfZGF5X29mX3dlZWsnKSwgMiwgbWVkaWFuKQpFZl9kYXlfb2Zfd2VlayA8LSBleHAoRWZfZGF5X29mX3dlZWsgLSBtZWFuKEVmX2RheV9vZl93ZWVrKSArIG1lYW4obG9nKGRhdGEkYmlydGhzX3JlbGF0aXZlMTAwKSkpCkVmNCA8LSBhcHBseShzdWJzZXQoZHJhd3M2LCB2YXJpYWJsZT0nYmV0YV9mNCcpLCAyLCBtZWRpYW4pKnNkKGxvZyhkYXRhJGJpcnRoc19yZWxhdGl2ZTEwMCkpCkVmNCA8LSBleHAoRWY0KSoxMDAKcGYgPC0gZGF0YSAlPiUKICBtdXRhdGUoRWYgPSBFZikgJT4lCiAgZ2dwbG90KGFlcyh4PWRhdGUsIHk9YmlydGhzX3JlbGF0aXZlMTAwKSkgKyBnZW9tX3BvaW50KGNvbG9yPXNldDFbMl0sIGFscGhhPTAuMikgKwogIGdlb21fbGluZShhZXMoeT1FZiksIGNvbG9yPXNldDFbMV0sIGFscGhhPTAuNzUpICsKICBsYWJzKHg9IkRhdGUiLCB5PSJSZWxhdGl2ZSBudW1iZXIgb2YgYmlydGhzIikKcGYxIDwtIGRhdGEgJT4lCiAgbXV0YXRlKEVmMSA9IEVmMSkgJT4lCiAgZ2dwbG90KGFlcyh4PWRhdGUsIHk9YmlydGhzX3JlbGF0aXZlMTAwKSkgKyBnZW9tX3BvaW50KGNvbG9yPXNldDFbMl0sIGFscGhhPTAuMikgKwogIGdlb21fbGluZShhZXMoeT1FZjEpLCBjb2xvcj1zZXQxWzFdKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PTEwMCwgY29sb3I9J2dyYXknKSArCiAgbGFicyh4PSJEYXRlIiwgeT0iUmVsYXRpdmUgbnVtYmVyIG9mIGJpcnRocyIpCnBmMiA8LSBkYXRhICU+JQogIG11dGF0ZShFZjIgPSBFZjIpICU+JQogIGdyb3VwX2J5KGRheV9vZl95ZWFyMikgJT4lCiAgc3VtbWFyaXNlKG1lYW5iaXJ0aHM9bWVhbihiaXJ0aHNfcmVsYXRpdmUxMDApLCBtZWFuRWYyPW1lYW4oRWYyKSkgJT4lCiAgZ2dwbG90KGFlcyh4PWFzLkRhdGUoIjE5ODctMTItMzEiKStkYXlfb2ZfeWVhcjIsIHk9bWVhbmJpcnRocykpICsgZ2VvbV9wb2ludChjb2xvcj1zZXQxWzJdLCBhbHBoYT0wLjIpICsKICBzY2FsZV94X2RhdGUoZGF0ZV9icmVha3MgPSAiMSBtb250aCIsIGRhdGVfbGFiZWxzID0gIiViIikgKwogIGdlb21fbGluZShhZXMoeT1tZWFuRWYyKSwgY29sb3I9c2V0MVsxXSkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdD0xMDAsIGNvbG9yPSdncmF5JykgKwogIGxhYnMoeD0iRGF0ZSIsIHk9IlJlbGF0aXZlIG51bWJlciBvZiBiaXJ0aHMgb2YgeWVhciIpCnBmMyA8LSBnZ3Bsb3QoZGF0YT1kYXRhLCBhZXMoeD1kYXlfb2Zfd2VlaywgeT1iaXJ0aHNfcmVsYXRpdmUxMDApKSArIGdlb21fcG9pbnQoY29sb3I9c2V0MVsyXSwgYWxwaGE9MC4yKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IDE6NywgbGFiZWxzPWMoJ01vbicsJ1R1ZScsJ1dlZCcsJ1RodScsJ0ZyaScsJ1NhdCcsJ1N1bicpKSArCiAgZ2VvbV9saW5lKGRhdGE9ZGF0YS5mcmFtZSh4PTE6Nyx5PUVmX2RheV9vZl93ZWVrKSwgYWVzKHg9eCwgeT1FZl9kYXlfb2Zfd2VlayksIGNvbG9yPXNldDFbMV0pICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9MTAwLCBjb2xvcj0nZ3JheScpICsKICBsYWJzKHg9IkRhdGUiLCB5PSJSZWxhdGl2ZSBudW1iZXIgb2YgYmlydGhzIG9mIHdlZWsiKQpmMTMgPC0gZGF0YSAlPiUgZmlsdGVyKHllYXI9PTE5ODgpJT4lc2VsZWN0KGRheSxkYXRlKSU+JW11dGF0ZSh5PUVmNCklPiVmaWx0ZXIoZGF5PT0xMykKcGYyYiA8LWRhdGEuZnJhbWUoeD1hcy5EYXRlKCIxOTg4LTAxLTAxIikrMDozNjUsIHk9RWY0KSAlPiUKICBnZ3Bsb3QoYWVzKHg9eCx5PXkpKSArIGdlb21fbGluZShjb2xvcj1zZXQxWzFdKSArCiAgc2NhbGVfeF9kYXRlKGRhdGVfYnJlYWtzID0gIjEgbW9udGgiLCBkYXRlX2xhYmVscyA9ICIlYiIpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9MTAwLCBjb2xvcj0nZ3JheScpICsKICBsYWJzKHg9IkRhdGUiLCB5PSJSZWxhdGl2ZSBudW1iZXIgb2YgYmlydGhzIG9mIHllYXIiKSArCiAgYW5ub3RhdGUoInRleHQiLHg9YXMuRGF0ZSgiMTk4OC0wMS0wMSIpLHk9RWY0WzFdLTEsbGFiZWw9Ik5ldyB5ZWFyIikgKwogIGFubm90YXRlKCJ0ZXh0Iix4PWFzLkRhdGUoIjE5ODgtMDItMTQiKSx5PUVmNFs0NV0rMS41LGxhYmVsPSJWYWxlbnRpbmUncyBkYXkiKSArCiAgYW5ub3RhdGUoInRleHQiLHg9YXMuRGF0ZSgiMTk4OC0wMi0yOSIpLHk9RWY0WzYwXS0yLjUsbGFiZWw9IkxlYXAgZGF5IikgKwogIGFubm90YXRlKCJ0ZXh0Iix4PWFzLkRhdGUoIjE5ODgtMDQtMDEiKSx5PUVmNFs5Ml0tMS41LGxhYmVsPSJBcHJpbCAxc3QiKSArIAogIGFubm90YXRlKCJ0ZXh0Iix4PWFzLkRhdGUoIjE5ODgtMDctMDQiKSx5PUVmNFsxODZdLTEuNSxsYWJlbD0iSW5kZXBlbmRlbmNlIGRheSIpICsKICBhbm5vdGF0ZSgidGV4dCIseD1hcy5EYXRlKCIxOTg4LTEwLTMxIikseT1FZjRbMzA1XS0xLjUsbGFiZWw9IkhhbGxvd2VlbiIpICsgCiAgYW5ub3RhdGUoInRleHQiLHg9YXMuRGF0ZSgiMTk4OC0xMi0yNCIpLHk9RWY0WzM2MF0tMS41LGxhYmVsPSJDaHJpc3RtYXMiKSArCiAgZ2VvbV9wb2ludChkYXRhPWYxMyxhZXMoeD1kYXRlLHk9eSksIHNpemU9Mywgc2hhcGU9MSkKKHBmICsgcGYxKSAvIChwZjIgKyBwZjMpIC8gcGYyYgpgYGAKClRoZSBzaG9ydCBzYW1wbGluZyByZXN1bHQgbG9va3MgcmVhc29uYWJsZSBhbmQgdGh1cyB0aGUgcHJvYmxlbSBpcwpub3QgaW4gYWRkaW5nIHRoZSBkYXkgb2YgeWVhciBlZmZlY3QgaXRzZWxmLiAgSW4gdGhlIGJvdHRvbSBwbG90LAp0aGUgY2lyY2xlcyBtYXJrIDEzdGggZGF5IG9mIGVhY2ggbW9udGguIFJlc3VsdHMgbG9vayBzaW1pbGFyIHRvCm91ciBwcmV2aW91cyBhbmFseXNlcyAsIHNvIGl0IHNlZW1zIHRoZSBkYXkgb3IgeWVhciBlZmZlY3QgbW9kZWwKY29tcG9uZW50IGlzIHdvcmtpbmcgYXMgaXQgc2hvdWxkLCBidXQgdGhlcmUgd2FzIHNvbWUgcHJvYmxlbSB3aXRoCm91ciBSSFMgaW1wbGVtZW50YXRpb24uIEFzIHRoZXJlIGlzIG1vcmUgdmFyaWF0aW9uIGluIHRoZSBkYXkgb2YKeWVhciBlZmZlY3RzIHRoYW4gd2Ugd291bGQgaG9wZSwgd2UgZGlkIHNvbWUgYWRkaXRpb25hbCBleHBlcmltZW50cwp3aXRoIGRpZmZlcmVudCBwcmlvcnMgZm9yIHRoZSBkYXkgb2YgeWVhciBlZmZlY3QgKGRvdWJsZQpleHBvbmVudGlhbCwgQ2F1Y2h5IGFuZCBTdHVkZW50J3MgdCB3aXRoIHVua25vd24gZGVncmVlcyBvZiBmcmVlZG9tCmFzIG1vZGVscyA2YiwgNmMsIDZkKSwgYnV0IGRlY2lkZWQgaXQncyBiZXR0ZXIgdG8gYWRkIG90aGVyCmNvbXBvbmVudHMgYmVmb3JlIGludmVzdGluZyB0aGF0IHBhcnQgbW9yZSB0aG9yb3VnaGx5LgoKIyMjIE1vZGVsIDc6IGxvbmcgdGVybSBzbW9vdGggKyBzZWFzb25hbCArIHdlZWtkYXkgKyBkYXkgb2YgeWVhciBub3JtYWwgKyBmbG9hdGluZyBzcGVjaWFsIGRheXMKCldlIGNhbiBzZWUgaW4gdGhlIG1vZGVsIDYgcmVzdWx0cyB0aGF0IGRheSBvZiB5ZWFyIGVmZmVjdHMgaGF2ZQpzb21lIGRpcHMgaW4gdGhlIHJlbGF0aXZlIG51bWJlciBvZiBiaXJ0aHMgdGhhdCBhcmUgc3ByZWFkIG92ZXIgYQp3ZWVrLiBGcm9tIHByZXZpb3VzIGFuYWx5c2Ugd2Uga25vdyB0aGVzZSBjb3JyZXNwb25kIHRvIGhvbGlkYXlzCnRoYXQgYXJlIG5vdCBvbiBhIHNwZWNpZmljIGRheSBvZiB5ZWFyLCBidXQgYXJlIGZvciBleGFtcGxlIG9uIHRoZQpsYXN0IE1vbmRheSBvZiBNYXkuIFdlIGNhbGwgdGhlc2UgZmxvYXRpbmcgc3BlY2lhbCBkYXlzIGFuZCBpbmNsdWRlCk1lbW9yaWFsIGRheSAobGFzdCBNb25kYXkgb2YgTWF5KSwgTGFib3IgZGF5IChmaXJzdCBNb25kYXkgb2YKU2VwdGVtYmVyLCBhbmQgd2UgaW5jbHVkZSBhbHNvIHRoZSBmb2xsb3dpbmcgVHVlc2RheSksIGFuZApUaGFua3NnaXZpbmcgKGZvdXJ0aCBUaHVyc2RheSBvZiBOb3ZlbWJlciwgYW5kIHdlIGluY2x1ZGUgYWxzbyB0aGUKZm9sbG93aW5nIEZyaWRheSkuCgpDb21waWxlIFN0YW4gbW9kZWwgNyBbZ3BiZjcuc3Rhbl0oaHR0cHM6Ly9naXRodWIuY29tL2F2ZWh0YXJpL2Nhc2VzdHVkaWVzL2Jsb2IvbWFzdGVyL0JpcnRoZGF5cy9ncGJmNy5zdGFuKQoKYGBge3IgbW9kZWw3LCByZXN1bHRzPSdoaWRlJ30KbW9kZWw3IDwtIGNtZHN0YW5fbW9kZWwoc3Rhbl9maWxlID0gcm9vdCgiQmlydGhkYXlzIiwgImdwYmY3LnN0YW4iKSwKICAgICAgICAgICAgICAgICAgICAgICAgaW5jbHVkZV9wYXRocyA9IHJvb3QoIkJpcnRoZGF5cyIpKQpgYGAKCkZsb2F0aW5nIHNwZWNpYWwgZGF5cwoKYGBge3IgfQojIE1lbW9yaWFsIGRheQptZW1vcmlhbF9kYXlzIDwtIHdpdGgoZGF0YSx3aGljaChtb250aD09NSZkYXlfb2Zfd2Vlaz09MSZkYXk+PTI1KSkKIyBMYWJvciBkYXkKbGFib3JfZGF5cyA8LSB3aXRoKGRhdGEsd2hpY2gobW9udGg9PTkmZGF5X29mX3dlZWs9PTEmZGF5PD03KSkKbGFib3JfZGF5cyA8LSBjKGxhYm9yX2RheXMsIGxhYm9yX2RheXMrMSkKIyBUaGFua3NnaXZpbmcKdGhhbmtzZ2l2aW5nX2RheXMgPC0gd2l0aChkYXRhLHdoaWNoKG1vbnRoPT0xMSZkYXlfb2Zfd2Vlaz09NCZkYXk+PTIyJmRheTw9MjgpKQp0aGFua3NnaXZpbmdfZGF5cyA8LSBjKHRoYW5rc2dpdmluZ19kYXlzLCB0aGFua3NnaXZpbmdfZGF5cysxKQpgYGAKCkRhdGEgdG8gYmUgcGFzc2VkIHRvIFN0YW4KCmBgYHtyIH0Kc3RhbmRhdGE3IDwtIGxpc3QoeD1kYXRhJGlkLAogICAgICAgICAgICAgICAgICB5PWxvZyhkYXRhJGJpcnRoc19yZWxhdGl2ZTEwMCksCiAgICAgICAgICAgICAgICAgIE49bGVuZ3RoKGRhdGEkaWQpLAogICAgICAgICAgICAgICAgICBjX2YxPTEuNSwgIyBmYWN0b3IgYyBvZiBiYXNpcyBmdW5jdGlvbnMgZm9yIEdQIGZvciBmMQogICAgICAgICAgICAgICAgICBNX2YxPTIwLCAgIyBudW1iZXIgb2YgYmFzaXMgZnVuY3Rpb25zIGZvciBHUCBmb3IgZjEKICAgICAgICAgICAgICAgICAgSl9mMj0yMCwgICMgbnVtYmVyIG9mIGJhc2lzIGZ1bmN0aW9ucyBmb3IgcGVyaW9kaWMgZjIKICAgICAgICAgICAgICAgICAgZGF5X29mX3dlZWs9ZGF0YSRkYXlfb2Zfd2VlaywKICAgICAgICAgICAgICAgICAgZGF5X29mX3llYXI9ZGF0YSRkYXlfb2ZfeWVhcjIsICMgMXN0IE1hcmNoID0gNjEgZXZlcnkgeWVhcgogICAgICAgICAgICAgICAgICBtZW1vcmlhbF9kYXlzPW1lbW9yaWFsX2RheXMsCiAgICAgICAgICAgICAgICAgIGxhYm9yX2RheXM9bGFib3JfZGF5cywKICAgICAgICAgICAgICAgICAgdGhhbmtzZ2l2aW5nX2RheXM9dGhhbmtzZ2l2aW5nX2RheXMpCmBgYAoKT3B0aW1pemluZyBpcyBmYXN0ZXIgdGhhbiBzYW1wbGluZyAoYWx0aG91Z2ggdGhpcyByZXN1bHQgY2FuIGJlCnVzZWZ1bCBpbiBhIHF1aWNrIHdvcmtmbG93LCB0aGUgcmVzdWx0IHNob3VsZCBub3QgYmUgdXNlZCBhcyB0aGUKZmluYWwgcmVzdWx0KS4KCmBgYHtyIG9wdDcsIHJlc3VsdHM9J2hpZGUnfQpvcHQ3IDwtIG1vZGVsNyRvcHRpbWl6ZShkYXRhPXN0YW5kYXRhNywgaW5pdD0wLCBhbGdvcml0aG09J2xiZmdzJywKICAgICAgICAgICAgICAgICAgICAgICAgaGlzdG9yeT0xMDAsIHRvbF9vYmo9MTApCmBgYAoKQ2hlY2sgd2hldGhlciBwYXJhbWV0ZXJzIGhhdmUgcmVhc29uYWJsZSB2YWx1ZXMKCmBgYHtyIH0Kb2RyYXdzNyA8LSBvcHQ3JGRyYXdzKCkKc3Vic2V0KG9kcmF3czcsIHZhcmlhYmxlPWMoJ2ludGVyY2VwdCcsJ3NpZ21hXycsJ2xlbmd0aHNjYWxlXycsJ3NpZ21hJyksIHJlZ2V4PVRSVUUpCnN1YnNldChvZHJhd3M3LCB2YXJpYWJsZT1jKCdiZXRhX2YzJykpCkVmNCA8LSBhcy5udW1lcmljKHN1YnNldChvZHJhd3M3LCB2YXJpYWJsZT0nYmV0YV9mNCcpKSpzZChsb2coZGF0YSRiaXJ0aHNfcmVsYXRpdmUxMDApKQpFZjQgPC0gZXhwKEVmNCkqMTAwCmRhdGEuZnJhbWUoeD1hcy5EYXRlKCIxOTg4LTAxLTAxIikrMDozNjUsIHk9RWY0KSAlPiUKICBnZ3Bsb3QoYWVzKHg9eCx5PXkpKSArIGdlb21fbGluZShjb2xvcj1zZXQxWzFdKSArCiAgc2NhbGVfeF9kYXRlKGRhdGVfYnJlYWtzID0gIjEgbW9udGgiLCBkYXRlX2xhYmVscyA9ICIlYiIpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9MTAwLCBjb2xvcj0nZ3JheScpICsKICBsYWJzKHg9IkRhdGUiLCB5PSJSZWxhdGl2ZSBudW1iZXIgb2YgYmlydGhzIG9mIHllYXIiKQpgYGAKClNhbXBsZSBzaG9ydCBjaGFpbnMgdXNpbmcgdGhlIGVhcmx5IHN0b3BwZWQgb3B0aW1pemF0aW9uIHJlc3VsdCBhcwppbml0aWFsIHZhbHVlcyAoYWx0aG91Z2ggdGhlIHJlc3VsdCBmcm9tIHNob3J0IGNoYWlucyBjYW4gYmUgdXNlZnVsCmluIGEgcXVpY2sgd29ya2Zsb3csIHRoZSByZXN1bHQgc2hvdWxkIG5vdCBiZSB1c2VkIGFzIHRoZSBmaW5hbApyZXN1bHQpLgoKYGBge3IgfQppbml0NyA8LSBzYXBwbHkoYygnbGVuZ3Roc2NhbGVfZjEnLCdsZW5ndGhzY2FsZV9mMicsCiAgICAgICAgICAgICAgICAgICdzaWdtYV9mMScsJ3NpZ21hX2YyJywnc2lnbWFfZjQnLCdzaWdtYScsCiAgICAgICAgICAgICAgICAgICdiZXRhX2YxJywnYmV0YV9mMicsJ2JldGFfZjMnLCdiZXRhX2Y0JywnYmV0YV9mNScpLAogICAgICAgICAgICAgICAgZnVuY3Rpb24odmFyaWFibGUpIHthcy5udW1lcmljKHN1YnNldChvZHJhd3M3LCB2YXJpYWJsZT12YXJpYWJsZSkpfSkKYGBgCmBgYHtyIGZpdDcsIHJlc3VsdHM9J2hpZGUnfQpmaXQ3IDwtIG1vZGVsNyRzYW1wbGUoZGF0YT1zdGFuZGF0YTcsIGl0ZXJfd2FybXVwPTEwMCwgaXRlcl9zYW1wbGluZz0xMDAsIGNoYWlucz00LCBwYXJhbGxlbF9jaGFpbnM9NCwKICAgICAgICAgICAgICAgICAgICAgIGluaXQ9ZnVuY3Rpb24oKSB7IGluaXQ3IH0sIHJlZnJlc2g9MTApCmBgYAoKQ2hlY2sgd2hldGhlciBwYXJhbWV0ZXJzIGhhdmUgcmVhc29uYWJsZSB2YWx1ZXMKCmBgYHtyIH0KZHJhd3M3IDwtIGZpdDckZHJhd3MoKQpzdW1tYXJpc2VfZHJhd3Moc3Vic2V0KGRyYXdzNywgdmFyaWFibGU9Yygnc2lnbWFfJywnbGVuZ3Roc2NhbGVfJywnc2lnbWEnKSwgcmVnZXg9VFJVRSkpCnN1bW1hcmlzZV9kcmF3cyhzdWJzZXQoZHJhd3M3LCB2YXJpYWJsZT1jKCdiZXRhX2YzJykpKQpgYGAKCkNvbXBhcmUgdGhlIG1vZGVsIHRvIHRoZSBkYXRhCgpgYGB7ciB9CmRyYXdzNyA8LSBhc19kcmF3c19tYXRyaXgoZHJhd3M3KQpFZiA8LSBleHAoYXBwbHkoc3Vic2V0KGRyYXdzNywgdmFyaWFibGU9J2YnKSwgMiwgbWVkaWFuKSkKRWYxIDwtIGFwcGx5KHN1YnNldChkcmF3czcsIHZhcmlhYmxlPSdmMScpLCAyLCBtZWRpYW4pCkVmMSA8LSBleHAoRWYxIC0gbWVhbihFZjEpICsgbWVhbihsb2coZGF0YSRiaXJ0aHNfcmVsYXRpdmUxMDApKSkKRWYyIDwtIGFwcGx5KHN1YnNldChkcmF3czcsIHZhcmlhYmxlPSdmMicpLCAyLCBtZWRpYW4pCkVmMiA8LSBleHAoRWYyIC0gbWVhbihFZjIpICsgbWVhbihsb2coZGF0YSRiaXJ0aHNfcmVsYXRpdmUxMDApKSkKRWZfZGF5X29mX3dlZWsgPC0gYXBwbHkoc3Vic2V0KGRyYXdzNywgdmFyaWFibGU9J2ZfZGF5X29mX3dlZWsnKSwgMiwgbWVkaWFuKQpFZl9kYXlfb2Zfd2VlayA8LSBleHAoRWZfZGF5X29mX3dlZWsgLSBtZWFuKEVmX2RheV9vZl93ZWVrKSArIG1lYW4obG9nKGRhdGEkYmlydGhzX3JlbGF0aXZlMTAwKSkpCkVmNCA8LSBhcHBseShzdWJzZXQoZHJhd3M3LCB2YXJpYWJsZT0nYmV0YV9mNCcpLCAyLCBtZWRpYW4pKnNkKGxvZyhkYXRhJGJpcnRoc19yZWxhdGl2ZTEwMCkpCkVmNCA8LSBleHAoRWY0KSoxMDAKRWZsb2F0cyA8LSBhcHBseShzdWJzZXQoZHJhd3M3LCB2YXJpYWJsZT0nYmV0YV9mNScpLCAyLCBtZWRpYW4pKnNkKGxvZyhkYXRhJGJpcnRoc19yZWxhdGl2ZTEwMCkpCkVmbG9hdHMgPC0gZXhwKEVmbG9hdHMpKjEwMApmbG9hdHMxOTg4PC1jKG1lbW9yaWFsX2RheXNbMjBdLCBsYWJvcl9kYXlzW2MoMjAsNDApXSwgdGhhbmtzZ2l2aW5nX2RheXNbYygyMCw0MCldKS02OTM5CkVmNGZsb2F0IDwtIEVmNApFZjRmbG9hdFtmbG9hdHMxOTg4XSA8LSBFZjRmbG9hdFtmbG9hdHMxOTg4XSpFZmxvYXRzW2MoMSwyLDIsMywzKV0vMTAwCnBmIDwtIGRhdGEgJT4lCiAgbXV0YXRlKEVmID0gRWYpICU+JQogIGdncGxvdChhZXMoeD1kYXRlLCB5PWJpcnRoc19yZWxhdGl2ZTEwMCkpICsgZ2VvbV9wb2ludChjb2xvcj1zZXQxWzJdLCBhbHBoYT0wLjIpICsKICBnZW9tX2xpbmUoYWVzKHk9RWYpLCBjb2xvcj1zZXQxWzFdLCBhbHBoYT0wLjc1KSArCiAgbGFicyh4PSJEYXRlIiwgeT0iUmVsYXRpdmUgbnVtYmVyIG9mIGJpcnRocyIpCnBmMSA8LSBkYXRhICU+JQogIG11dGF0ZShFZjEgPSBFZjEpICU+JQogIGdncGxvdChhZXMoeD1kYXRlLCB5PWJpcnRoc19yZWxhdGl2ZTEwMCkpICsgZ2VvbV9wb2ludChjb2xvcj1zZXQxWzJdLCBhbHBoYT0wLjIpICsKICBnZW9tX2xpbmUoYWVzKHk9RWYxKSwgY29sb3I9c2V0MVsxXSkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdD0xMDAsIGNvbG9yPSdncmF5JykgKwogIGxhYnMoeD0iRGF0ZSIsIHk9IlJlbGF0aXZlIG51bWJlciBvZiBiaXJ0aHMiKQpwZjIgPC0gZGF0YSAlPiUKICBtdXRhdGUoRWYyID0gRWYyKSAlPiUKICBncm91cF9ieShkYXlfb2ZfeWVhcjIpICU+JQogIHN1bW1hcmlzZShtZWFuYmlydGhzPW1lYW4oYmlydGhzX3JlbGF0aXZlMTAwKSwgbWVhbkVmMj1tZWFuKEVmMikpICU+JQogIGdncGxvdChhZXMoeD1hcy5EYXRlKCIxOTg3LTEyLTMxIikrZGF5X29mX3llYXIyLCB5PW1lYW5iaXJ0aHMpKSArIGdlb21fcG9pbnQoY29sb3I9c2V0MVsyXSwgYWxwaGE9MC4yKSArCiAgc2NhbGVfeF9kYXRlKGRhdGVfYnJlYWtzID0gIjEgbW9udGgiLCBkYXRlX2xhYmVscyA9ICIlYiIpICsKICBnZW9tX2xpbmUoYWVzKHk9bWVhbkVmMiksIGNvbG9yPXNldDFbMV0pICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9MTAwLCBjb2xvcj0nZ3JheScpICsKICBsYWJzKHg9IkRhdGUiLCB5PSJSZWxhdGl2ZSBudW1iZXIgb2YgYmlydGhzIG9mIHllYXIiKQpwZjMgPC0gZ2dwbG90KGRhdGE9ZGF0YSwgYWVzKHg9ZGF5X29mX3dlZWssIHk9YmlydGhzX3JlbGF0aXZlMTAwKSkgKyBnZW9tX3BvaW50KGNvbG9yPXNldDFbMl0sIGFscGhhPTAuMikgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSAxOjcsIGxhYmVscz1jKCdNb24nLCdUdWUnLCdXZWQnLCdUaHUnLCdGcmknLCdTYXQnLCdTdW4nKSkgKwogIGdlb21fbGluZShkYXRhPWRhdGEuZnJhbWUoeD0xOjcseT1FZl9kYXlfb2Zfd2VlayksIGFlcyh4PXgsIHk9RWZfZGF5X29mX3dlZWspLCBjb2xvcj1zZXQxWzFdKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PTEwMCwgY29sb3I9J2dyYXknKSArCiAgbGFicyh4PSJEYXRlIiwgeT0iUmVsYXRpdmUgbnVtYmVyIG9mIGJpcnRocyBvZiB3ZWVrIikKZjEzIDwtIGRhdGEgJT4lIGZpbHRlcih5ZWFyPT0xOTg4KSU+JXNlbGVjdChkYXksZGF0ZSklPiVtdXRhdGUoeT1FZjRmbG9hdCklPiVmaWx0ZXIoZGF5PT0xMykKCnBmMmIgPC1kYXRhLmZyYW1lKHg9YXMuRGF0ZSgiMTk4OC0wMS0wMSIpKzA6MzY1LCB5PUVmNGZsb2F0KSAlPiUKICBnZ3Bsb3QoYWVzKHg9eCx5PXkpKSArIGdlb21fbGluZShjb2xvcj1zZXQxWzFdKSArCiAgc2NhbGVfeF9kYXRlKGRhdGVfYnJlYWtzID0gIjEgbW9udGgiLCBkYXRlX2xhYmVscyA9ICIlYiIpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9MTAwLCBjb2xvcj0nZ3JheScpICsKICBsYWJzKHg9IkRhdGUiLCB5PSJSZWxhdGl2ZSBudW1iZXIgb2YgYmlydGhzIG9mIHllYXIiKSArCiAgYW5ub3RhdGUoInRleHQiLHg9YXMuRGF0ZSgiMTk4OC0wMS0wMSIpLHk9RWY0ZmxvYXRbMV0tMSxsYWJlbD0iTmV3IHllYXIiKSArCiAgYW5ub3RhdGUoInRleHQiLHg9YXMuRGF0ZSgiMTk4OC0wMi0xNCIpLHk9RWY0ZmxvYXRbNDVdKzEuNSxsYWJlbD0iVmFsZW50aW5lJ3MgZGF5IikgKwogIGFubm90YXRlKCJ0ZXh0Iix4PWFzLkRhdGUoIjE5ODgtMDItMjkiKSx5PUVmNGZsb2F0WzYwXS0yLjUsbGFiZWw9IkxlYXAgZGF5IikgKwogIGFubm90YXRlKCJ0ZXh0Iix4PWFzLkRhdGUoIjE5ODgtMDQtMDEiKSx5PUVmNGZsb2F0WzkyXS0xLjUsbGFiZWw9IkFwcmlsIDFzdCIpICsgCiAgYW5ub3RhdGUoInRleHQiLHg9YXMuRGF0ZSgiMTk4OC0wNy0wNCIpLHk9RWY0ZmxvYXRbMTg2XS0xLjUsbGFiZWw9IkluZGVwZW5kZW5jZSBkYXkiKSArCiAgYW5ub3RhdGUoInRleHQiLHg9YXMuRGF0ZSgiMTk4OC0xMC0zMSIpLHk9RWY0ZmxvYXRbMzA1XS0xLjUsbGFiZWw9IkhhbGxvd2VlbiIpICsgCiAgYW5ub3RhdGUoInRleHQiLHg9YXMuRGF0ZSgiMTk4OC0xMi0yNCIpLHk9RWY0ZmxvYXRbMzYwXS0yLGxhYmVsPSJDaHJpc3RtYXMiKSArCiAgYW5ub3RhdGUoInRleHQiLHg9YXMuRGF0ZSgiMTk4OC0wNS0zMCIpLHk9RWY0ZmxvYXRbMTUxXS0xLjUsbGFiZWw9Ik1lbW9yaWFsIGRheSIpICsKICBhbm5vdGF0ZSgidGV4dCIseD1hcy5EYXRlKCIxOTg4LTA5LTA1IikseT1FZjRmbG9hdFsyNDldLTEuNSxsYWJlbD0iTGFib3IgZGF5IikgKyAKICBhbm5vdGF0ZSgidGV4dCIseD1hcy5EYXRlKCIxOTg4LTExLTI0IikseT1FZjRmbG9hdFszMjldLTEsbGFiZWw9IlRoYW5rc2dpdmluZyIpKwogIGdlb21fcG9pbnQoZGF0YT1mMTMsYWVzKHg9ZGF0ZSx5PXkpLCBzaXplPTMsIHNoYXBlPTEpCihwZiArIHBmMSkgLyAocGYyICsgcGYzKSAvIChwZjJiKQpgYGAKClRoZSBkYXkgb2YgeWVhciBhbmQgZmxvYXRpbmcgc3BlY2lhbCBkYXkgZWZmZWN0cyBhcmUgc2hvd24gZm9yIHllYXIKMTk4OCAod2hpY2ggaXMgYWxzbyBhIGxlYXAgeWVhcikgYW5kIHRoZSByZXN1bHRzIHNlZW0gcmVhc29uYWJsZS4KCiMjIyBNb2RlbCA4OiBsb25nIHRlcm0gc21vb3RoICsgc2Vhc29uYWwgKyB3ZWVrZGF5IHdpdGggdGltZSBkZXBlbmRlbnQgbWFnbml0dWRlICsgZGF5IG9mIHllYXIgKyBzcGVjaWFsCgpBcyB0aGUgZGF5IG9mIHllYXIgYW5kIGZsb2F0aW5nIGRheSBlZmZlY3RzIHdvcmsgd2VsbCwgd2UnbGwgYWRkCnRoZSB0aW1lIGRlcGVuZGVudCBkYXkgb2Ygd2VlayBlZmZlY3QgYmFjayB0byB0aGUgbW9kZWwuCgpDb21waWxlIFN0YW4gbW9kZWwgOCBbZ3BiZjguc3Rhbl0oaHR0cHM6Ly9naXRodWIuY29tL2F2ZWh0YXJpL2Nhc2VzdHVkaWVzL2Jsb2IvbWFzdGVyL0JpcnRoZGF5cy9ncGJmOC5zdGFuKQoKYGBge3IgbW9kZWw4LCByZXN1bHRzPSdoaWRlJ30KbW9kZWw4IDwtIGNtZHN0YW5fbW9kZWwoc3Rhbl9maWxlID0gcm9vdCgiQmlydGhkYXlzIiwgImdwYmY4LnN0YW4iKSwKICAgICAgICAgICAgICAgICAgICAgICAgaW5jbHVkZV9wYXRocyA9IHJvb3QoIkJpcnRoZGF5cyIpKQpgYGAKCkZsb2F0aW5nIHNwZWNpYWwgZGF5cwoKYGBge3IgfQojIE1lbW9yaWFsIGRheQptZW1vcmlhbF9kYXlzIDwtIHdpdGgoZGF0YSx3aGljaChtb250aD09NSZkYXlfb2Zfd2Vlaz09MSZkYXk+PTI1KSkKIyBMYWJvciBkYXkKbGFib3JfZGF5cyA8LSB3aXRoKGRhdGEsd2hpY2gobW9udGg9PTkmZGF5X29mX3dlZWs9PTEmZGF5PD03KSkKbGFib3JfZGF5cyA8LSBjKGxhYm9yX2RheXMsIGxhYm9yX2RheXMrMSkKIyBUaGFua3NnaXZpbmcKdGhhbmtzZ2l2aW5nX2RheXMgPC0gd2l0aChkYXRhLHdoaWNoKG1vbnRoPT0xMSZkYXlfb2Zfd2Vlaz09NCZkYXk+PTIyJmRheTw9MjgpKQp0aGFua3NnaXZpbmdfZGF5cyA8LSBjKHRoYW5rc2dpdmluZ19kYXlzLCB0aGFua3NnaXZpbmdfZGF5cysxKQpgYGAKCkRhdGEgdG8gYmUgcGFzc2VkIHRvIFN0YW4KCmBgYHtyIH0Kc3RhbmRhdGE4IDwtIGxpc3QoeD1kYXRhJGlkLAogICAgICAgICAgICAgICAgICB5PWxvZyhkYXRhJGJpcnRoc19yZWxhdGl2ZTEwMCksCiAgICAgICAgICAgICAgICAgIE49bGVuZ3RoKGRhdGEkaWQpLAogICAgICAgICAgICAgICAgICBjX2YxPTEuNSwgIyBmYWN0b3IgYyBvZiBiYXNpcyBmdW5jdGlvbnMgZm9yIEdQIGZvciBmMQogICAgICAgICAgICAgICAgICBNX2YxPTIwLCAgIyBudW1iZXIgb2YgYmFzaXMgZnVuY3Rpb25zIGZvciBHUCBmb3IgZjEKICAgICAgICAgICAgICAgICAgSl9mMj0yMCwgICMgbnVtYmVyIG9mIGJhc2lzIGZ1bmN0aW9ucyBmb3IgcGVyaW9kaWMgZjIKICAgICAgICAgICAgICAgICAgY19nMz0xLjUsICMgZmFjdG9yIGMgb2YgYmFzaXMgZnVuY3Rpb25zIGZvciBHUCBmb3IgZzMKICAgICAgICAgICAgICAgICAgTV9nMz01LCAgICMgbnVtYmVyIG9mIGJhc2lzIGZ1bmN0aW9ucyBmb3IgR1AgZm9yIGczCiAgICAgICAgICAgICAgICAgIGRheV9vZl93ZWVrPWRhdGEkZGF5X29mX3dlZWssCiAgICAgICAgICAgICAgICAgIGRheV9vZl95ZWFyPWRhdGEkZGF5X29mX3llYXIyLCAjIDFzdCBNYXJjaCA9IDYxIGV2ZXJ5IHllYXIKICAgICAgICAgICAgICAgICAgbWVtb3JpYWxfZGF5cz1tZW1vcmlhbF9kYXlzLAogICAgICAgICAgICAgICAgICBsYWJvcl9kYXlzPWxhYm9yX2RheXMsCiAgICAgICAgICAgICAgICAgIHRoYW5rc2dpdmluZ19kYXlzPXRoYW5rc2dpdmluZ19kYXlzKQpgYGAKCk9wdGltaXppbmcgaXMgZmFzdGVyIHRoYW4gc2FtcGxpbmcgKGFsdGhvdWdoIHRoaXMgcmVzdWx0IGNhbiBiZQp1c2VmdWwgaW4gYSBxdWljayB3b3JrZmxvdywgdGhlIHJlc3VsdCBzaG91bGQgbm90IGJlIHVzZWQgYXMgdGhlCmZpbmFsIHJlc3VsdCkuCgpgYGB7ciBvcHQ4LCByZXN1bHRzPSdoaWRlJ30Kb3B0OCA8LSBtb2RlbDgkb3B0aW1pemUoZGF0YT1zdGFuZGF0YTgsIGluaXQ9MC4xLCBhbGdvcml0aG09J2xiZmdzJywKICAgICAgICAgICAgICAgICAgICAgICAgaGlzdG9yeT0xMDAsIHRvbF9vYmo9MTApCmBgYAoKQ2hlY2sgd2hldGhlciBwYXJhbWV0ZXJzIGhhdmUgcmVhc29uYWJsZSB2YWx1ZXMKCmBgYHtyIH0Kb2RyYXdzOCA8LSBvcHQ4JGRyYXdzKCkKc3Vic2V0KG9kcmF3czgsIHZhcmlhYmxlPWMoJ3NpZ21hXycsJ2xlbmd0aHNjYWxlXycsJ3NpZ21hJyksIHJlZ2V4PVRSVUUpCnN1YnNldChvZHJhd3M4LCB2YXJpYWJsZT1jKCdiZXRhX2YzJykpCkVmNCA8LSBhcy5udW1lcmljKHN1YnNldChvZHJhd3M4LCB2YXJpYWJsZT0nYmV0YV9mNCcpKSpzZChsb2coZGF0YSRiaXJ0aHNfcmVsYXRpdmUxMDApKQpFZjQgPC0gZXhwKEVmNCkqMTAwCmRhdGEuZnJhbWUoeD1hcy5EYXRlKCIxOTg4LTAxLTAxIikrMDozNjUsIHk9RWY0KSAlPiUKICBnZ3Bsb3QoYWVzKHg9eCx5PXkpKSArIGdlb21fbGluZShjb2xvcj1zZXQxWzFdKSArCiAgc2NhbGVfeF9kYXRlKGRhdGVfYnJlYWtzID0gIjEgbW9udGgiLCBkYXRlX2xhYmVscyA9ICIlYiIpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9MTAwLCBjb2xvcj0nZ3JheScpICsKICBsYWJzKHg9IkRhdGUiLCB5PSJSZWxhdGl2ZSBudW1iZXIgb2YgYmlydGhzIG9mIHllYXIiKQpgYGAKCkNvbXBhcmUgdGhlIG1vZGVsIHRvIHRoZSBkYXRhCgpgYGB7ciB9CkVmIDwtIGV4cChhcy5udW1lcmljKHN1YnNldChvZHJhd3M4LCB2YXJpYWJsZT0nZicpKSkKRWYxIDwtIGFzLm51bWVyaWMoc3Vic2V0KG9kcmF3czgsIHZhcmlhYmxlPSdmMScpKQpFZjEgPC0gZXhwKEVmMSAtIG1lYW4oRWYxKSArIG1lYW4obG9nKGRhdGEkYmlydGhzX3JlbGF0aXZlMTAwKSkpCkVmMiA8LSBhcy5udW1lcmljKHN1YnNldChvZHJhd3M4LCB2YXJpYWJsZT0nZjInKSkKRWYyIDwtIGV4cChFZjIgLSBtZWFuKEVmMikgKyBtZWFuKGxvZyhkYXRhJGJpcnRoc19yZWxhdGl2ZTEwMCkpKQpFZl9kYXlfb2Zfd2VlayA8LSBhcy5udW1lcmljKHN1YnNldChvZHJhd3M4LCB2YXJpYWJsZT0nZl9kYXlfb2Zfd2VlaycpKQpFZl9kYXlfb2Zfd2VlayA8LSBleHAoRWZfZGF5X29mX3dlZWsgLSBtZWFuKEVmX2RheV9vZl93ZWVrKSArIG1lYW4obG9nKGRhdGEkYmlydGhzX3JlbGF0aXZlMTAwKSkpCkVmMyA8LSBhcy5udW1lcmljKHN1YnNldChvZHJhd3M4LCB2YXJpYWJsZT0nZjMnKSkKRWYzIDwtIGV4cChFZjMgLSBtZWFuKEVmMykgKyBtZWFuKGxvZyhkYXRhJGJpcnRoc19yZWxhdGl2ZTEwMCkpKQpFZjQgPC0gYXMubnVtZXJpYyhzdWJzZXQob2RyYXdzOCwgdmFyaWFibGU9J2JldGFfZjQnKSkqc2QobG9nKGRhdGEkYmlydGhzX3JlbGF0aXZlMTAwKSkKRWY0IDwtIGV4cChFZjQpKjEwMApFZmxvYXRzIDwtIGFzLm51bWVyaWMoc3Vic2V0KG9kcmF3czgsIHZhcmlhYmxlPSdiZXRhX2Y1JykpKnNkKGxvZyhkYXRhJGJpcnRoc19yZWxhdGl2ZTEwMCkpCkVmbG9hdHMgPC0gZXhwKEVmbG9hdHMpKjEwMApmbG9hdHMxOTg4PC1jKG1lbW9yaWFsX2RheXNbMjBdLCBsYWJvcl9kYXlzW2MoMjAsNDApXSwgdGhhbmtzZ2l2aW5nX2RheXNbYygyMCw0MCldKS02OTM5CkVmNGZsb2F0IDwtIEVmNApFZjRmbG9hdFtmbG9hdHMxOTg4XSA8LSBFZjRmbG9hdFtmbG9hdHMxOTg4XSpFZmxvYXRzW2MoMSwyLDIsMywzKV0vMTAwCnBmIDwtIGRhdGEgJT4lCiAgbXV0YXRlKEVmID0gRWYpICU+JQogIGdncGxvdChhZXMoeD1kYXRlLCB5PWJpcnRoc19yZWxhdGl2ZTEwMCkpICsgZ2VvbV9wb2ludChjb2xvcj1zZXQxWzJdLCBhbHBoYT0wLjIpICsKICBnZW9tX3BvaW50KGFlcyh5PUVmKSwgY29sb3I9c2V0MVsxXSwgYWxwaGE9MC4yKSArCiAgbGFicyh4PSJEYXRlIiwgeT0iUmVsYXRpdmUgbnVtYmVyIG9mIGJpcnRocyIpCnBmMSA8LSBkYXRhICU+JQogIG11dGF0ZShFZjEgPSBFZjEpICU+JQogIGdncGxvdChhZXMoeD1kYXRlLCB5PWJpcnRoc19yZWxhdGl2ZTEwMCkpICsgZ2VvbV9wb2ludChjb2xvcj1zZXQxWzJdLCBhbHBoYT0wLjIpICsKICBnZW9tX2xpbmUoYWVzKHk9RWYxKSwgY29sb3I9c2V0MVsxXSkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdD0xMDAsIGNvbG9yPSdncmF5JykgKwogIGxhYnMoeD0iRGF0ZSIsIHk9IlJlbGF0aXZlIG51bWJlciBvZiBiaXJ0aHMiKQpwZjIgPC0gZGF0YSAlPiUKICBtdXRhdGUoRWYyID0gRWYyKSAlPiUKICBncm91cF9ieShkYXlfb2ZfeWVhcjIpICU+JQogIHN1bW1hcmlzZShtZWFuYmlydGhzPW1lYW4oYmlydGhzX3JlbGF0aXZlMTAwKSwgbWVhbkVmMj1tZWFuKEVmMikpICU+JQogIGdncGxvdChhZXMoeD1hcy5EYXRlKCIxOTg3LTEyLTMxIikrZGF5X29mX3llYXIyLCB5PW1lYW5iaXJ0aHMpKSArIGdlb21fcG9pbnQoY29sb3I9c2V0MVsyXSwgYWxwaGE9MC4yKSArCiAgc2NhbGVfeF9kYXRlKGRhdGVfYnJlYWtzID0gIjEgbW9udGgiLCBkYXRlX2xhYmVscyA9ICIlYiIpICsKICBnZW9tX2xpbmUoYWVzKHk9bWVhbkVmMiksIGNvbG9yPXNldDFbMV0pICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9MTAwLCBjb2xvcj0nZ3JheScpICsKICBsYWJzKHg9IkRhdGUiLCB5PSJSZWxhdGl2ZSBudW1iZXIgb2YgYmlydGhzIG9mIHllYXIiKQpwZjMgPC0gZ2dwbG90KGRhdGE9ZGF0YSwgYWVzKHg9ZGF5X29mX3dlZWssIHk9YmlydGhzX3JlbGF0aXZlMTAwKSkgKyBnZW9tX3BvaW50KGNvbG9yPXNldDFbMl0sIGFscGhhPTAuMikgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSAxOjcsIGxhYmVscz1jKCdNb24nLCdUdWUnLCdXZWQnLCdUaHUnLCdGcmknLCdTYXQnLCdTdW4nKSkgKwogIGdlb21fbGluZShkYXRhPWRhdGEuZnJhbWUoeD0xOjcseT1FZl9kYXlfb2Zfd2VlayksIGFlcyh4PXgsIHk9RWZfZGF5X29mX3dlZWspLCBjb2xvcj1zZXQxWzFdKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PTEwMCwgY29sb3I9J2dyYXknKSArCiAgbGFicyh4PSJEYXRlIiwgeT0iUmVsYXRpdmUgbnVtYmVyIG9mIGJpcnRocyBvZiB3ZWVrIikKTj1sZW5ndGgoZGF0YSRpZCkKcGYzYiA8LSBkYXRhICU+JQogIG11dGF0ZShFZjMgPSBFZjMqRWYxLzEwMCkgJT4lCiAgZ2dwbG90KGFlcyh4PWRhdGUsIHk9YmlydGhzX3JlbGF0aXZlMTAwKSkgKyBnZW9tX3BvaW50KGNvbG9yPXNldDFbMl0sIGFscGhhPTAuMikgKwogIGdlb21fcG9pbnQoYWVzKHk9RWYzKSwgY29sb3I9c2V0MVsxXSwgc2l6ZT0wLjEpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9MTAwLCBjb2xvcj0nZ3JheScpICsKICBsYWJzKHg9IkRhdGUiLCB5PSJSZWxhdGl2ZSBudW1iZXIgb2YgYmlydGhzIikgKwogIGFubm90YXRlKCJ0ZXh0Iix4PWFzLkRhdGUoIjE5ODktMDgtMDEiKSx5PShFZjMqRWYxLzEwMClbYygoTi01KTooTi00KSwgTiwgTi02KV0sbGFiZWw9YygiTW9uIiwiVHVlIiwiU2F0IiwiU3VuIikpCmYxMyA8LSBkYXRhICU+JSBmaWx0ZXIoeWVhcj09MTk4OCklPiVzZWxlY3QoZGF5LGRhdGUpJT4lbXV0YXRlKHk9RWY0ZmxvYXQpJT4lZmlsdGVyKGRheT09MTMpCnBmMmIgPC1kYXRhLmZyYW1lKHg9YXMuRGF0ZSgiMTk4OC0wMS0wMSIpKzA6MzY1LCB5PUVmNGZsb2F0KSAlPiUKICBnZ3Bsb3QoYWVzKHg9eCx5PXkpKSArIGdlb21fbGluZShjb2xvcj1zZXQxWzFdKSArCiAgc2NhbGVfeF9kYXRlKGRhdGVfYnJlYWtzID0gIjEgbW9udGgiLCBkYXRlX2xhYmVscyA9ICIlYiIpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9MTAwLCBjb2xvcj0nZ3JheScpICsKICBsYWJzKHg9IkRhdGUiLCB5PSJSZWxhdGl2ZSBudW1iZXIgb2YgYmlydGhzIG9mIHllYXIiKSArCiAgYW5ub3RhdGUoInRleHQiLHg9YXMuRGF0ZSgiMTk4OC0wMS0wMSIpLHk9RWY0ZmxvYXRbMV0tMSxsYWJlbD0iTmV3IHllYXIiKSArCiAgYW5ub3RhdGUoInRleHQiLHg9YXMuRGF0ZSgiMTk4OC0wMi0xNCIpLHk9RWY0ZmxvYXRbNDVdKzEuNSxsYWJlbD0iVmFsZW50aW5lJ3MgZGF5IikgKwogIGFubm90YXRlKCJ0ZXh0Iix4PWFzLkRhdGUoIjE5ODgtMDItMjkiKSx5PUVmNGZsb2F0WzYwXS0yLjUsbGFiZWw9IkxlYXAgZGF5IikgKwogIGFubm90YXRlKCJ0ZXh0Iix4PWFzLkRhdGUoIjE5ODgtMDQtMDEiKSx5PUVmNGZsb2F0WzkyXS0xLjUsbGFiZWw9IkFwcmlsIDFzdCIpICsgCiAgYW5ub3RhdGUoInRleHQiLHg9YXMuRGF0ZSgiMTk4OC0wNy0wNCIpLHk9RWY0ZmxvYXRbMTg2XS0xLjUsbGFiZWw9IkluZGVwZW5kZW5jZSBkYXkiKSArCiAgYW5ub3RhdGUoInRleHQiLHg9YXMuRGF0ZSgiMTk4OC0xMC0zMSIpLHk9RWY0ZmxvYXRbMzA1XS0xLjUsbGFiZWw9IkhhbGxvd2VlbiIpICsgCiAgYW5ub3RhdGUoInRleHQiLHg9YXMuRGF0ZSgiMTk4OC0xMi0yNCIpLHk9RWY0ZmxvYXRbMzYwXS0yLGxhYmVsPSJDaHJpc3RtYXMiKSArCiAgYW5ub3RhdGUoInRleHQiLHg9YXMuRGF0ZSgiMTk4OC0wNS0zMCIpLHk9RWY0ZmxvYXRbMTUxXS0yLGxhYmVsPSJNZW1vcmlhbCBkYXkiKSArCiAgYW5ub3RhdGUoInRleHQiLHg9YXMuRGF0ZSgiMTk4OC0wOS0wNSIpLHk9RWY0ZmxvYXRbMjQ5XS0xLjUsbGFiZWw9IkxhYm9yIGRheSIpICsgCiAgYW5ub3RhdGUoInRleHQiLHg9YXMuRGF0ZSgiMTk4OC0xMS0yNCIpLHk9RWY0ZmxvYXRbMzI5XS0xLGxhYmVsPSJUaGFua3NnaXZpbmciKSsKICBnZW9tX3BvaW50KGRhdGE9ZjEzLGFlcyh4PWRhdGUseT15KSwgc2l6ZT0zLCBzaGFwZT0xKQoocGYgKyBwZjEpIC8gKHBmMiArIHBmM2IpIC8gKHBmMmIpCmBgYAoKU2FtcGxlIHNob3J0IGNoYWlucyB1c2luZyB0aGUgZWFybHkgc3RvcHBlZCBvcHRpbWl6YXRpb24gcmVzdWx0IGFzCmluaXRpYWwgdmFsdWVzIChhbHRob3VnaCB0aGUgcmVzdWx0IGZyb20gc2hvcnQgY2hhaW5zIGNhbiBiZSB1c2VmdWwKaW4gYSBxdWljayB3b3JrZmxvdywgdGhlIHJlc3VsdCBzaG91bGQgbm90IGJlIHVzZWQgYXMgdGhlIGZpbmFsCnJlc3VsdCkuCgpgYGB7ciB9CmluaXQ4IDwtIHNhcHBseShjKCdsZW5ndGhzY2FsZV9mMScsJ2xlbmd0aHNjYWxlX2YyJywnbGVuZ3Roc2NhbGVfZzMnLAogICAgICAgICAgICAgICAgICAnc2lnbWFfZjEnLCdzaWdtYV9mMicsJ3NpZ21hX2czJywnc2lnbWFfZjQnLCdzaWdtYScsCiAgICAgICAgICAgICAgICAgICdiZXRhX2YxJywnYmV0YV9mMicsJ2JldGFfZjMnLCdiZXRhX2czJywnYmV0YV9mNCcsJ2JldGFfZjUnKSwKICAgICAgICAgICAgICAgIGZ1bmN0aW9uKHZhcmlhYmxlKSB7YXMubnVtZXJpYyhzdWJzZXQob2RyYXdzOCwgdmFyaWFibGU9dmFyaWFibGUpKX0pCmBgYApgYGB7ciBmaXQ4LCByZXN1bHRzPSdoaWRlJ30KZml0OCA8LSBtb2RlbDgkc2FtcGxlKGRhdGE9c3RhbmRhdGE4LCBpdGVyX3dhcm11cD0xMDAsIGl0ZXJfc2FtcGxpbmc9MTAwLCBjaGFpbnM9NCwgcGFyYWxsZWxfY2hhaW5zPTQsCiAgICAgICAgICAgICAgICAgICAgICBpbml0PWZ1bmN0aW9uKCkgeyBpbml0OCB9LCByZWZyZXNoPTEwKQpgYGAKCkNoZWNrIHdoZXRoZXIgcGFyYW1ldGVycyBoYXZlIHJlYXNvbmFibGUgdmFsdWVzCgpgYGB7ciB9CmRyYXdzOCA8LSBmaXQ4JGRyYXdzKCkKc3VtbWFyaXNlX2RyYXdzKHN1YnNldChkcmF3czgsIHZhcmlhYmxlPWMoJ3NpZ21hXycsJ2xlbmd0aHNjYWxlXycsJ3NpZ21hJyksIHJlZ2V4PVRSVUUpKQpzdW1tYXJpc2VfZHJhd3Moc3Vic2V0KGRyYXdzOCwgdmFyaWFibGU9YygnYmV0YV9mMycpKSkKYGBgCgpDb21wYXJlIHRoZSBtb2RlbCB0byB0aGUgZGF0YQoKYGBge3IgfQpkcmF3czggPC0gYXNfZHJhd3NfbWF0cml4KGRyYXdzOCkKRWYgPC0gZXhwKGFwcGx5KHN1YnNldChkcmF3czgsIHZhcmlhYmxlPSdmJyksIDIsIG1lZGlhbikpCkVmMSA8LSBhcHBseShzdWJzZXQoZHJhd3M4LCB2YXJpYWJsZT0nZjEnKSwgMiwgbWVkaWFuKQpFZjEgPC0gZXhwKEVmMSAtIG1lYW4oRWYxKSArIG1lYW4obG9nKGRhdGEkYmlydGhzX3JlbGF0aXZlMTAwKSkpCkVmMiA8LSBhcHBseShzdWJzZXQoZHJhd3M4LCB2YXJpYWJsZT0nZjInKSwgMiwgbWVkaWFuKQpFZjIgPC0gZXhwKEVmMiAtIG1lYW4oRWYyKSArIG1lYW4obG9nKGRhdGEkYmlydGhzX3JlbGF0aXZlMTAwKSkpCkVmX2RheV9vZl93ZWVrIDwtIGFwcGx5KHN1YnNldChkcmF3czgsIHZhcmlhYmxlPSdmX2RheV9vZl93ZWVrJyksIDIsIG1lZGlhbikKRWZfZGF5X29mX3dlZWsgPC0gZXhwKEVmX2RheV9vZl93ZWVrIC0gbWVhbihFZl9kYXlfb2Zfd2VlaykgKyBtZWFuKGxvZyhkYXRhJGJpcnRoc19yZWxhdGl2ZTEwMCkpKQpFZjMgPC0gYXBwbHkoc3Vic2V0KGRyYXdzOCwgdmFyaWFibGU9J2YzJyksIDIsIG1lZGlhbikKRWYzIDwtIGV4cChFZjMgLSBtZWFuKEVmMykgKyBtZWFuKGxvZyhkYXRhJGJpcnRoc19yZWxhdGl2ZTEwMCkpKQpFZjQgPC0gYXBwbHkoc3Vic2V0KGRyYXdzOCwgdmFyaWFibGU9J2JldGFfZjQnKSwgMiwgbWVkaWFuKSpzZChsb2coZGF0YSRiaXJ0aHNfcmVsYXRpdmUxMDApKQpFZjQgPC0gZXhwKEVmNCkqMTAwCkVmbG9hdHMgPC0gYXBwbHkoc3Vic2V0KGRyYXdzOCwgdmFyaWFibGU9J2JldGFfZjUnKSwgMiwgbWVkaWFuKSpzZChsb2coZGF0YSRiaXJ0aHNfcmVsYXRpdmUxMDApKQpFZmxvYXRzIDwtIGV4cChFZmxvYXRzKSoxMDAKZmxvYXRzMTk4ODwtYyhtZW1vcmlhbF9kYXlzWzIwXSwgbGFib3JfZGF5c1tjKDIwLDQwKV0sIHRoYW5rc2dpdmluZ19kYXlzW2MoMjAsNDApXSktNjkzOQpFZjRmbG9hdCA8LSBFZjQKRWY0ZmxvYXRbZmxvYXRzMTk4OF0gPC0gRWY0ZmxvYXRbZmxvYXRzMTk4OF0qRWZsb2F0c1tjKDEsMiwyLDMsMyldLzEwMApwZiA8LSBkYXRhICU+JQogIG11dGF0ZShFZiA9IEVmKSAlPiUKICBnZ3Bsb3QoYWVzKHg9ZGF0ZSwgeT1iaXJ0aHNfcmVsYXRpdmUxMDApKSArIGdlb21fcG9pbnQoY29sb3I9c2V0MVsyXSwgYWxwaGE9MC4yKSArCiAgZ2VvbV9wb2ludChhZXMoeT1FZiksIGNvbG9yPXNldDFbMV0sIGFscGhhPTAuMikgKwogIGxhYnMoeD0iRGF0ZSIsIHk9IlJlbGF0aXZlIG51bWJlciBvZiBiaXJ0aHMiKQpwZjEgPC0gZGF0YSAlPiUKICBtdXRhdGUoRWYxID0gRWYxKSAlPiUKICBnZ3Bsb3QoYWVzKHg9ZGF0ZSwgeT1iaXJ0aHNfcmVsYXRpdmUxMDApKSArIGdlb21fcG9pbnQoY29sb3I9c2V0MVsyXSwgYWxwaGE9MC4yKSArCiAgZ2VvbV9saW5lKGFlcyh5PUVmMSksIGNvbG9yPXNldDFbMV0pICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9MTAwLCBjb2xvcj0nZ3JheScpICsKICBsYWJzKHg9IkRhdGUiLCB5PSJSZWxhdGl2ZSBudW1iZXIgb2YgYmlydGhzIikKcGYyIDwtIGRhdGEgJT4lCiAgbXV0YXRlKEVmMiA9IEVmMikgJT4lCiAgZ3JvdXBfYnkoZGF5X29mX3llYXIyKSAlPiUKICBzdW1tYXJpc2UobWVhbmJpcnRocz1tZWFuKGJpcnRoc19yZWxhdGl2ZTEwMCksIG1lYW5FZjI9bWVhbihFZjIpKSAlPiUKICBnZ3Bsb3QoYWVzKHg9YXMuRGF0ZSgiMTk4Ny0xMi0zMSIpK2RheV9vZl95ZWFyMiwgeT1tZWFuYmlydGhzKSkgKyBnZW9tX3BvaW50KGNvbG9yPXNldDFbMl0sIGFscGhhPTAuMikgKwogIHNjYWxlX3hfZGF0ZShkYXRlX2JyZWFrcyA9ICIxIG1vbnRoIiwgZGF0ZV9sYWJlbHMgPSAiJWIiKSArCiAgZ2VvbV9saW5lKGFlcyh5PW1lYW5FZjIpLCBjb2xvcj1zZXQxWzFdKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PTEwMCwgY29sb3I9J2dyYXknKSArCiAgbGFicyh4PSJEYXRlIiwgeT0iUmVsYXRpdmUgbnVtYmVyIG9mIGJpcnRocyBvZiB5ZWFyIikKcGYzIDwtIGdncGxvdChkYXRhPWRhdGEsIGFlcyh4PWRheV9vZl93ZWVrLCB5PWJpcnRoc19yZWxhdGl2ZTEwMCkpICsgZ2VvbV9wb2ludChjb2xvcj1zZXQxWzJdLCBhbHBoYT0wLjIpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gMTo3LCBsYWJlbHM9YygnTW9uJywnVHVlJywnV2VkJywnVGh1JywnRnJpJywnU2F0JywnU3VuJykpICsKICBnZW9tX2xpbmUoZGF0YT1kYXRhLmZyYW1lKHg9MTo3LHk9RWZfZGF5X29mX3dlZWspLCBhZXMoeD14LCB5PUVmX2RheV9vZl93ZWVrKSwgY29sb3I9c2V0MVsxXSkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdD0xMDAsIGNvbG9yPSdncmF5JykgKwogIGxhYnMoeD0iRGF0ZSIsIHk9IlJlbGF0aXZlIG51bWJlciBvZiBiaXJ0aHMgb2Ygd2VlayIpCk49bGVuZ3RoKGRhdGEkaWQpCnBmM2IgPC0gZGF0YSAlPiUKICBtdXRhdGUoRWYzID0gRWYzKkVmMS8xMDApICU+JQogIGdncGxvdChhZXMoeD1kYXRlLCB5PWJpcnRoc19yZWxhdGl2ZTEwMCkpICsgZ2VvbV9wb2ludChjb2xvcj1zZXQxWzJdLCBhbHBoYT0wLjIpICsKICBnZW9tX3BvaW50KGFlcyh5PUVmMyksIGNvbG9yPXNldDFbMV0sIHNpemU9MC4xKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PTEwMCwgY29sb3I9J2dyYXknKSArCiAgbGFicyh4PSJEYXRlIiwgeT0iUmVsYXRpdmUgbnVtYmVyIG9mIGJpcnRocyIpICsKICBhbm5vdGF0ZSgidGV4dCIseD1hcy5EYXRlKCIxOTg5LTA4LTAxIikseT0oRWYzKkVmMS8xMDApW2MoKE4tNSk6KE4tNCksIE4sIE4tNildLGxhYmVsPWMoIk1vbiIsIlR1ZSIsIlNhdCIsIlN1biIpKQpmMTMgPC0gZGF0YSAlPiUgZmlsdGVyKHllYXI9PTE5ODgpJT4lc2VsZWN0KGRheSxkYXRlKSU+JW11dGF0ZSh5PUVmNGZsb2F0KSU+JWZpbHRlcihkYXk9PTEzKQpwZjJiIDwtZGF0YS5mcmFtZSh4PWFzLkRhdGUoIjE5ODgtMDEtMDEiKSswOjM2NSwgeT1FZjRmbG9hdCkgJT4lCiAgZ2dwbG90KGFlcyh4PXgseT15KSkgKyBnZW9tX2xpbmUoY29sb3I9c2V0MVsxXSkgKwogIHNjYWxlX3hfZGF0ZShkYXRlX2JyZWFrcyA9ICIxIG1vbnRoIiwgZGF0ZV9sYWJlbHMgPSAiJWIiKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PTEwMCwgY29sb3I9J2dyYXknKSArCiAgbGFicyh4PSJEYXRlIiwgeT0iUmVsYXRpdmUgbnVtYmVyIG9mIGJpcnRocyBvZiB5ZWFyIikgKwogIGFubm90YXRlKCJ0ZXh0Iix4PWFzLkRhdGUoIjE5ODgtMDEtMDEiKSx5PUVmNGZsb2F0WzFdLTEsbGFiZWw9Ik5ldyB5ZWFyIikgKwogIGFubm90YXRlKCJ0ZXh0Iix4PWFzLkRhdGUoIjE5ODgtMDItMTQiKSx5PUVmNGZsb2F0WzQ1XSsxLjUsbGFiZWw9IlZhbGVudGluZSdzIGRheSIpICsKICBhbm5vdGF0ZSgidGV4dCIseD1hcy5EYXRlKCIxOTg4LTAyLTI5IikseT1FZjRmbG9hdFs2MF0tMi41LGxhYmVsPSJMZWFwIGRheSIpICsKICBhbm5vdGF0ZSgidGV4dCIseD1hcy5EYXRlKCIxOTg4LTA0LTAxIikseT1FZjRmbG9hdFs5Ml0tMS41LGxhYmVsPSJBcHJpbCAxc3QiKSArIAogIGFubm90YXRlKCJ0ZXh0Iix4PWFzLkRhdGUoIjE5ODgtMDctMDQiKSx5PUVmNGZsb2F0WzE4Nl0tMS41LGxhYmVsPSJJbmRlcGVuZGVuY2UgZGF5IikgKwogIGFubm90YXRlKCJ0ZXh0Iix4PWFzLkRhdGUoIjE5ODgtMTAtMzEiKSx5PUVmNGZsb2F0WzMwNV0tMS41LGxhYmVsPSJIYWxsb3dlZW4iKSArIAogIGFubm90YXRlKCJ0ZXh0Iix4PWFzLkRhdGUoIjE5ODgtMTItMjQiKSx5PUVmNGZsb2F0WzM2MF0tMixsYWJlbD0iQ2hyaXN0bWFzIikgKwogIGFubm90YXRlKCJ0ZXh0Iix4PWFzLkRhdGUoIjE5ODgtMDUtMzAiKSx5PUVmNGZsb2F0WzE1MV0tMixsYWJlbD0iTWVtb3JpYWwgZGF5IikgKwogIGFubm90YXRlKCJ0ZXh0Iix4PWFzLkRhdGUoIjE5ODgtMDktMDUiKSx5PUVmNGZsb2F0WzI0OV0tMS41LGxhYmVsPSJMYWJvciBkYXkiKSArIAogIGFubm90YXRlKCJ0ZXh0Iix4PWFzLkRhdGUoIjE5ODgtMTEtMjQiKSx5PUVmNGZsb2F0WzMyOV0tMSxsYWJlbD0iVGhhbmtzZ2l2aW5nIikrCiAgZ2VvbV9wb2ludChkYXRhPWYxMyxhZXMoeD1kYXRlLHk9eSksIHNpemU9Mywgc2hhcGU9MSkKKHBmICsgcGYxKSAvIChwZjIgKyBwZjNiKSAvIChwZjJiKQpgYGAKClRoZSBpbmZlcmVuY2UgZm9yIHRoZSBtb2RlbCB3b3JrcyBmaW5lLCB3aGljaCBoaW50cyB0aGF0IG91ciBSSFMKaW1wbGVtZW50YXRpb24gZm9yIHRoZSBtb2RlbCA1IHdhcyB3cm9uZyBvciBoYWQgdmVyeSBkaWZmaWN1bHQKcG9zdGVyaW9yLiBCZWZvcmUgdGVzdGluZyBSSFMgYWdhaW4sIHdlJ2xsIHRlc3Qgd2l0aCBhbiBlYXNpZXIgdG8KaW1wbGVtZW50IFN0dWRlbnQncyAkdCQgcHJpb3Igd2hldGhlciBsb25nIHRhaWxlZCBwcmlvciBmb3IgZGF5IG9mCnllYXIgZWZmZWN0IGlzIHJlYXNvbmFibGUuIFRoZXNlIGV4cGVyaW1lbnRzIGhlbHAgYWxzbyB0byBmaW5kIG91dAp3aGV0aGVyIHRoZSBkYXkgb2YgeWVhciBlZmZlY3QgaXMgc2Vuc2l0aXZlIHRvIHRoZSBwcmlvciBjaG9pY2UuCgoKIyMjIE1vZGVsIDgrdF9udTogZGF5IG9mIHllYXIgZWZmZWN0IHdpdGggU3R1ZGVudCdzIHQgcHJpb3IKCkNvbXBpbGUgU3RhbiBtb2RlbCA4ICsgdF9udSBbZ3BiZjh0bnUuc3Rhbl0oaHR0cHM6Ly9naXRodWIuY29tL2F2ZWh0YXJpL2Nhc2VzdHVkaWVzL2Jsb2IvbWFzdGVyL0JpcnRoZGF5cy9ncGJmOHRudS5zdGFuKQoKYGBge3IgbW9kZWw4dG51LCByZXN1bHRzPSdoaWRlJ30KbW9kZWw4dG51IDwtIGNtZHN0YW5fbW9kZWwoc3Rhbl9maWxlID0gcm9vdCgiQmlydGhkYXlzIiwgImdwYmY4dG51LnN0YW4iKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5jbHVkZV9wYXRocyA9IHJvb3QoIkJpcnRoZGF5cyIpKQpgYGAKCk9wdGltaXppbmcgaXMgZmFzdGVyIHRoYW4gc2FtcGxpbmcgKGFsdGhvdWdoIHRoaXMgcmVzdWx0IGNhbiBiZQp1c2VmdWwgaW4gYSBxdWljayB3b3JrZmxvdywgdGhlIHJlc3VsdCBzaG91bGQgbm90IGJlIHVzZWQgYXMgdGhlCmZpbmFsIHJlc3VsdCkuCgpgYGB7ciBvcHQ4dG51LCByZXN1bHRzPSdoaWRlJ30Kb3B0OHRudSA8LSBtb2RlbDh0bnUkb3B0aW1pemUoZGF0YT1zdGFuZGF0YTgsIGluaXQ9MC4xLCBhbGdvcml0aG09J2xiZmdzJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGlzdG9yeT0xMDAsIHRvbF9vYmo9MTApCm9kcmF3czh0bnUgPC0gb3B0OHRudSRkcmF3cygpCmBgYAoKU2FtcGxlIHNob3J0IGNoYWlucyB1c2luZyB0aGUgZWFybHkgc3RvcHBlZCBvcHRpbWl6YXRpb24gcmVzdWx0IGFzCmluaXRpYWwgdmFsdWVzIChhbHRob3VnaCB0aGUgcmVzdWx0IGZyb20gc2hvcnQgY2hhaW5zIGNhbiBiZSB1c2VmdWwKaW4gYSBxdWljayB3b3JrZmxvdywgdGhlIHJlc3VsdCBzaG91bGQgbm90IGJlIHVzZWQgYXMgdGhlIGZpbmFsCnJlc3VsdCkuCgpgYGB7ciB9CmluaXQ4dG51IDwtIHNhcHBseShjKCdsZW5ndGhzY2FsZV9mMScsJ2xlbmd0aHNjYWxlX2YyJywnbGVuZ3Roc2NhbGVfZzMnLAogICAgICAgICAgICAgICAgICAnc2lnbWFfZjEnLCdzaWdtYV9mMicsJ3NpZ21hX2czJywnc2lnbWFfZjQnLCdudV9mNCcsJ3NpZ21hJywKICAgICAgICAgICAgICAgICAgJ2JldGFfZjEnLCdiZXRhX2YyJywnYmV0YV9mMycsJ2JldGFfZzMnLCdiZXRhX2Y0JywnYmV0YV9mNScpLAogICAgICAgICAgICAgICAgZnVuY3Rpb24odmFyaWFibGUpIHthcy5udW1lcmljKHN1YnNldChvZHJhd3M4dG51LCB2YXJpYWJsZT12YXJpYWJsZSkpfSkKYGBgCmBgYHtyIGZpdDh0bnUsIHJlc3VsdHM9J2hpZGUnfQpmaXQ4dG51IDwtIG1vZGVsOHRudSRzYW1wbGUoZGF0YT1zdGFuZGF0YTgsIGl0ZXJfd2FybXVwPTEwMCwgaXRlcl9zYW1wbGluZz0xMDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjaGFpbnM9NCwgcGFyYWxsZWxfY2hhaW5zPTQsCiAgICAgICAgICAgICAgICAgICAgICBpbml0PWZ1bmN0aW9uKCkgeyBpbml0OHRudSB9LCByZWZyZXNoPTEwKQpgYGAKCkNoZWNrIHdoZXRoZXIgcGFyYW1ldGVycyBoYXZlIHJlYXNvbmFibGUgdmFsdWVzCgpgYGB7ciB9CmRyYXdzOHRudSA8LSBmaXQ4dG51JGRyYXdzKCkKc3VtbWFyaXNlX2RyYXdzKHN1YnNldChkcmF3czh0bnUsIHZhcmlhYmxlPWMoJ2ludGVyY2VwdCcsJ3NpZ21hXycsJ2xlbmd0aHNjYWxlXycsJ3NpZ21hJywnbnVfJyksIHJlZ2V4PVRSVUUpKQpgYGAKClBvc3RlcmlvciBvZiBkZWdyZWVzIG9mIGZyZWVkb20gYG51X2Y0YCBpcyB2ZXJ5IGNsb3NlIHRvIDAuNSwgYW5kCnRodXMgdGhlIGRpc3RyaWJ1dGlvbiBoYXMgdGhpY2tlciB0YWlscyB0aGFuIENhdWNoeS4gVGhpcyBpcyBzdHJvbmcKZXZpZGVuY2UgdGhhdCB0aGUgZGlzdHJpYnV0aW9uIG9mIGRheSBvZiB5ZWFyIGVmZmVjdHMgaXMgZmFyIGZyb20Kbm9ybWFsLgpDb21wYXJlIHRoZSBtb2RlbCB0byB0aGUgZGF0YQoKYGBge3IgfQpkcmF3czggPC0gYXNfZHJhd3NfbWF0cml4KGRyYXdzOHRudSkKRWYgPC0gZXhwKGFwcGx5KHN1YnNldChkcmF3czgsIHZhcmlhYmxlPSdmJyksIDIsIG1lZGlhbikpCkVmMSA8LSBhcHBseShzdWJzZXQoZHJhd3M4LCB2YXJpYWJsZT0nZjEnKSwgMiwgbWVkaWFuKQpFZjEgPC0gZXhwKEVmMSAtIG1lYW4oRWYxKSArIG1lYW4obG9nKGRhdGEkYmlydGhzX3JlbGF0aXZlMTAwKSkpCkVmMiA8LSBhcHBseShzdWJzZXQoZHJhd3M4LCB2YXJpYWJsZT0nZjInKSwgMiwgbWVkaWFuKQpFZjIgPC0gZXhwKEVmMiAtIG1lYW4oRWYyKSArIG1lYW4obG9nKGRhdGEkYmlydGhzX3JlbGF0aXZlMTAwKSkpCkVmX2RheV9vZl93ZWVrIDwtIGFwcGx5KHN1YnNldChkcmF3czgsIHZhcmlhYmxlPSdmX2RheV9vZl93ZWVrJyksIDIsIG1lZGlhbikKRWZfZGF5X29mX3dlZWsgPC0gZXhwKEVmX2RheV9vZl93ZWVrIC0gbWVhbihFZl9kYXlfb2Zfd2VlaykgKyBtZWFuKGxvZyhkYXRhJGJpcnRoc19yZWxhdGl2ZTEwMCkpKQpFZjMgPC0gYXBwbHkoc3Vic2V0KGRyYXdzOCwgdmFyaWFibGU9J2YzJyksIDIsIG1lZGlhbikKRWYzIDwtIGV4cChFZjMgLSBtZWFuKEVmMykgKyBtZWFuKGxvZyhkYXRhJGJpcnRoc19yZWxhdGl2ZTEwMCkpKQpFZjQgPC0gYXBwbHkoc3Vic2V0KGRyYXdzOCwgdmFyaWFibGU9J2JldGFfZjQnKSwgMiwgbWVkaWFuKSpzZChsb2coZGF0YSRiaXJ0aHNfcmVsYXRpdmUxMDApKQpFZjQgPC0gZXhwKEVmNCkqMTAwCkVmbG9hdHMgPC0gYXBwbHkoc3Vic2V0KGRyYXdzOCwgdmFyaWFibGU9J2JldGFfZjUnKSwgMiwgbWVkaWFuKSpzZChsb2coZGF0YSRiaXJ0aHNfcmVsYXRpdmUxMDApKQpFZmxvYXRzIDwtIGV4cChFZmxvYXRzKSoxMDAKZmxvYXRzMTk4ODwtYyhtZW1vcmlhbF9kYXlzWzIwXSwgbGFib3JfZGF5c1tjKDIwLDQwKV0sIHRoYW5rc2dpdmluZ19kYXlzW2MoMjAsNDApXSktNjkzOQpFZjRmbG9hdCA8LSBFZjQKRWY0ZmxvYXRbZmxvYXRzMTk4OF0gPC0gRWY0ZmxvYXRbZmxvYXRzMTk4OF0qRWZsb2F0c1tjKDEsMiwyLDMsMyldLzEwMApwZiA8LSBkYXRhICU+JQogIG11dGF0ZShFZiA9IEVmKSAlPiUKICBnZ3Bsb3QoYWVzKHg9ZGF0ZSwgeT1iaXJ0aHNfcmVsYXRpdmUxMDApKSArIGdlb21fcG9pbnQoY29sb3I9c2V0MVsyXSwgYWxwaGE9MC4yKSArCiAgZ2VvbV9wb2ludChhZXMoeT1FZiksIGNvbG9yPXNldDFbMV0sIGFscGhhPTAuMikgKwogIGxhYnMoeD0iRGF0ZSIsIHk9IlJlbGF0aXZlIG51bWJlciBvZiBiaXJ0aHMiKQpwZjEgPC0gZGF0YSAlPiUKICBtdXRhdGUoRWYxID0gRWYxKSAlPiUKICBnZ3Bsb3QoYWVzKHg9ZGF0ZSwgeT1iaXJ0aHNfcmVsYXRpdmUxMDApKSArIGdlb21fcG9pbnQoY29sb3I9c2V0MVsyXSwgYWxwaGE9MC4yKSArCiAgZ2VvbV9saW5lKGFlcyh5PUVmMSksIGNvbG9yPXNldDFbMV0pICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9MTAwLCBjb2xvcj0nZ3JheScpICsKICBsYWJzKHg9IkRhdGUiLCB5PSJSZWxhdGl2ZSBudW1iZXIgb2YgYmlydGhzIikKcGYyIDwtIGRhdGEgJT4lCiAgbXV0YXRlKEVmMiA9IEVmMikgJT4lCiAgZ3JvdXBfYnkoZGF5X29mX3llYXIyKSAlPiUKICBzdW1tYXJpc2UobWVhbmJpcnRocz1tZWFuKGJpcnRoc19yZWxhdGl2ZTEwMCksIG1lYW5FZjI9bWVhbihFZjIpKSAlPiUKICBnZ3Bsb3QoYWVzKHg9YXMuRGF0ZSgiMTk4Ny0xMi0zMSIpK2RheV9vZl95ZWFyMiwgeT1tZWFuYmlydGhzKSkgKyBnZW9tX3BvaW50KGNvbG9yPXNldDFbMl0sIGFscGhhPTAuMikgKwogIHNjYWxlX3hfZGF0ZShkYXRlX2JyZWFrcyA9ICIxIG1vbnRoIiwgZGF0ZV9sYWJlbHMgPSAiJWIiKSArCiAgZ2VvbV9saW5lKGFlcyh5PW1lYW5FZjIpLCBjb2xvcj1zZXQxWzFdKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PTEwMCwgY29sb3I9J2dyYXknKSArCiAgbGFicyh4PSJEYXRlIiwgeT0iUmVsYXRpdmUgbnVtYmVyIG9mIGJpcnRocyBvZiB5ZWFyIikKcGYzIDwtIGdncGxvdChkYXRhPWRhdGEsIGFlcyh4PWRheV9vZl93ZWVrLCB5PWJpcnRoc19yZWxhdGl2ZTEwMCkpICsgZ2VvbV9wb2ludChjb2xvcj1zZXQxWzJdLCBhbHBoYT0wLjIpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gMTo3LCBsYWJlbHM9YygnTW9uJywnVHVlJywnV2VkJywnVGh1JywnRnJpJywnU2F0JywnU3VuJykpICsKICBnZW9tX2xpbmUoZGF0YT1kYXRhLmZyYW1lKHg9MTo3LHk9RWZfZGF5X29mX3dlZWspLCBhZXMoeD14LCB5PUVmX2RheV9vZl93ZWVrKSwgY29sb3I9c2V0MVsxXSkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdD0xMDAsIGNvbG9yPSdncmF5JykgKwogIGxhYnMoeD0iRGF0ZSIsIHk9IlJlbGF0aXZlIG51bWJlciBvZiBiaXJ0aHMgb2Ygd2VlayIpCk49bGVuZ3RoKGRhdGEkaWQpCnBmM2IgPC0gZGF0YSAlPiUKICBtdXRhdGUoRWYzID0gRWYzKkVmMS8xMDApICU+JQogIGdncGxvdChhZXMoeD1kYXRlLCB5PWJpcnRoc19yZWxhdGl2ZTEwMCkpICsgZ2VvbV9wb2ludChjb2xvcj1zZXQxWzJdLCBhbHBoYT0wLjIpICsKICBnZW9tX3BvaW50KGFlcyh5PUVmMyksIGNvbG9yPXNldDFbMV0sIHNpemU9MC4xKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PTEwMCwgY29sb3I9J2dyYXknKSArCiAgbGFicyh4PSJEYXRlIiwgeT0iUmVsYXRpdmUgbnVtYmVyIG9mIGJpcnRocyIpICsKICBhbm5vdGF0ZSgidGV4dCIseD1hcy5EYXRlKCIxOTg5LTA4LTAxIikseT0oRWYzKkVmMS8xMDApW2MoKE4tNSk6KE4tNCksIE4sIE4tNildLGxhYmVsPWMoIk1vbiIsIlR1ZSIsIlNhdCIsIlN1biIpKQpmMTMgPC0gZGF0YSAlPiUgZmlsdGVyKHllYXI9PTE5ODgpJT4lc2VsZWN0KGRheSxkYXRlKSU+JW11dGF0ZSh5PUVmNGZsb2F0KSU+JWZpbHRlcihkYXk9PTEzKQoKcGYyYiA8LWRhdGEuZnJhbWUoeD1hcy5EYXRlKCIxOTg4LTAxLTAxIikrMDozNjUsIHk9RWY0ZmxvYXQpICU+JQogIGdncGxvdChhZXMoeD14LHk9eSkpICsgZ2VvbV9saW5lKGNvbG9yPXNldDFbMV0pICsKICBzY2FsZV94X2RhdGUoZGF0ZV9icmVha3MgPSAiMSBtb250aCIsIGRhdGVfbGFiZWxzID0gIiViIikgKwogIGdlb21faGxpbmUoeWludGVyY2VwdD0xMDAsIGNvbG9yPSdncmF5JykgKwogIGxhYnMoeD0iRGF0ZSIsIHk9IlJlbGF0aXZlIG51bWJlciBvZiBiaXJ0aHMgb2YgeWVhciIpICsKICBhbm5vdGF0ZSgidGV4dCIseD1hcy5EYXRlKCIxOTg4LTAxLTAxIikseT1FZjRmbG9hdFsxXS0xLGxhYmVsPSJOZXcgeWVhciIpICsKICBhbm5vdGF0ZSgidGV4dCIseD1hcy5EYXRlKCIxOTg4LTAyLTE0IikseT1FZjRmbG9hdFs0NV0rMS41LGxhYmVsPSJWYWxlbnRpbmUncyBkYXkiKSArCiAgYW5ub3RhdGUoInRleHQiLHg9YXMuRGF0ZSgiMTk4OC0wMi0yOSIpLHk9RWY0ZmxvYXRbNjBdLTIuNSxsYWJlbD0iTGVhcCBkYXkiKSArCiAgYW5ub3RhdGUoInRleHQiLHg9YXMuRGF0ZSgiMTk4OC0wNC0wMSIpLHk9RWY0ZmxvYXRbOTJdLTEuNSxsYWJlbD0iQXByaWwgMXN0IikgKyAKICBhbm5vdGF0ZSgidGV4dCIseD1hcy5EYXRlKCIxOTg4LTA3LTA0IikseT1FZjRmbG9hdFsxODZdLTEuNSxsYWJlbD0iSW5kZXBlbmRlbmNlIGRheSIpICsKICBhbm5vdGF0ZSgidGV4dCIseD1hcy5EYXRlKCIxOTg4LTEwLTMxIikseT1FZjRmbG9hdFszMDVdLTEuNSxsYWJlbD0iSGFsbG93ZWVuIikgKyAKICBhbm5vdGF0ZSgidGV4dCIseD1hcy5EYXRlKCIxOTg4LTEyLTI0IikseT1FZjRmbG9hdFszNjBdLTIsbGFiZWw9IkNocmlzdG1hcyIpICsKICBhbm5vdGF0ZSgidGV4dCIseD1hcy5EYXRlKCIxOTg4LTA1LTMwIikseT1FZjRmbG9hdFsxNTFdLTIsbGFiZWw9Ik1lbW9yaWFsIGRheSIpICsKICBhbm5vdGF0ZSgidGV4dCIseD1hcy5EYXRlKCIxOTg4LTA5LTA1IikseT1FZjRmbG9hdFsyNDldLTEuNSxsYWJlbD0iTGFib3IgZGF5IikgKyAKICBhbm5vdGF0ZSgidGV4dCIseD1hcy5EYXRlKCIxOTg4LTExLTI0IikseT1FZjRmbG9hdFszMjldLTEsbGFiZWw9IlRoYW5rc2dpdmluZyIpKwogIGdlb21fcG9pbnQoZGF0YT1mMTMsYWVzKHg9ZGF0ZSx5PXkpLCBzaXplPTMsIHNoYXBlPTEpCihwZiArIHBmMSkgLyAocGYyICsgcGYzYikgLyAocGYyYikKYGBgCgpUaGUgb3RoZXIgZWZmZWN0cyBzZWVtIHRvIGJlIHF1aXRlIHNpbWlsYXIgYXMgd2l0aCB0aGUgcHJldmlvdXMKbW9kZWwsIGJ1dCB0aGUgZGF5IG9mIHllYXIgZWZmZWN0cyBhcmUgY2xlYXJseSBkaWZmZXJlbnQgd2l0aCBtb3N0CmRheXMgaGF2aW5nIG5vbi1kZXRlY3RhYmxlIGVmZmVjdC4gVGhlcmUgYXJlIGFsc28gZWZmZWN0cyB0aGF0CnNlZW1lZCB0byBiZSBxdWl0ZSBjbGVhciBpbiBub3JtYWwgcHJpb3IgbW9kZWwgc3VjaCBhcyAxM3RoIGRheSBvZgptb250aCBlZmZlY3QsIHdoaWNoIGlzIG5vdCB2aXNpYmxlIGFueW1vcmUuIEFzIHRoZSBwb3N0ZXJpb3Igb2YKZGVncmVlcyBvZiBmcmVlZG9tIGB0X251YCB3YXMgY29uY2VudHJhdGVkIGNsb3NlIHRvIDEsIGl0J3MgbGlrZWx5CnRoYXQgdGhlIG5vcm1hbCBwcmlvciBmb3IgZGF5IG9mIHllYXIgZWZmZWN0IGNhbid0IGJlIHRoZSBiZXN0LiBTbwpmYXIgd2UgaGFkbid0IHVzZWQgbW9kZWwgY29tcGFyaXNvbiBzdWNoIGFzIGxlYXZlLW9uZS1vdXQKY3Jvc3MtdmFsaWRhdGlvbiAoTE9PLUNWKSBhcyBlYWNoIGFkZGVkIGNvbXBvbmVudCBoYWQgcXVhbGl0YXRpdmVseQpiaWcgYW5kIHJlYXNvbmFibGUgZWZmZWN0LiBOb3cgYXMgZGF5IG9mIHllYXIgZWZmZWN0IGlzIHNlbnNpdGl2ZQp0byBwcmlvciBjaG9pY2UsIGJ1dCBpdCdzIG5vdCBjbGVhciBob3cgbXVjaCBiZXR0ZXIgJHRfXG51JCBwcmlvcgpkaXN0cmlidXRpb24gaXMgd2UgdXNlIExPTy1DViB0byBjb21wYXJlIHRoZSBtb2RlbHMuCgpgYGB7ciB9CmxvbzggPC0gZml0OCRsb28oKQpsb284dG51IDwtIGZpdDh0bnUkbG9vKCkKbG9vX2NvbXBhcmUobGlzdChgTW9kZWwgOCBub3JtYWxgPWxvbzgsYE1vZGVsIDggU3R1ZGVudFwncyB0YD1sb284dG51KSkKYGBgCgpBcyB3ZSBjb3VsZCBoYXZlIGV4cGVjdGVkIGJhc2VkIG9uIHRoZSBwb3N0ZXJpb3Igb2YgYG51X2Y0YApTdHVkZW50J3MgdCBwcmlvciBvbiBkYXkgb2YgeWVhciBlZmZlY3RzIGlzIGJldHRlci4gQXMgbG93IGRlZ3JlZXMKb2YgZnJlZWRvbSBpbmRpY2F0ZSBhIHRoaWNrIHRhaWxlZCBkaXN0cmlidXRpb24gZm9yIGRheSBvZiB5ZWFyCmVmZmVjdCBpcyBuZWVkZWQsIHdlIGRlY2lkZWQgdG8gdGVzdCBhZ2FpbiBSSFMgcHJpb3IuCgojIyMgTW9kZWwgOCtSSFM6IGRheSBvZiB5ZWFyIGVmZmVjdCB3aXRoIFJIUyBwcmlvcgoKTW9kZWwgNSBoYWQgUkhTIHByaW9yIGJ1dCB0aGUgcHJvYmxlbSB3YXMgdGhhdCBvcHRpbWl6YXRpb24gcmVzdWx0Cndhc24ndCBldmVuIGNsb3NlIHRvIHNlbnNpYmxlIGFuZCBNQ01DIHdhcyB2ZXJ5IHNsb3cuIEdpdmVuIHRoZQpvdGhlciBtb2RlbHMgd2Ugbm93IGtub3cgdGhhdCB0aGUgcHJvYmxlbSBpcyBub3QgaW4gYWRkaW5nIGRheSBvZgp5ZWFyIGVmZmVjdCBvciBjb21iaW5pbmcgaXQgd2l0aCB0aW1lIGRlcGVuZGVudCBtYWduaXR1ZGUgZm9yIHRoZQpkYXkgb2Ygd2VlayBlZmZlY3QuIEl0IHdhcyBlYXNpZXIgbm93IHRvIGZvY3VzIG9uIGZpZ3VyaW5nIG91dCB0aGUKcHJvYmxlbSBpbiBSSFMuIFNpbmNlIFJIUyBpcyBwcmVzZW50ZWQgYXMgYSBzY2FsZSBtaXh0dXJlIG9mCm5vcm1hbHMgaW52b2x2aW5nIGhpZXJhcmNoaWNhbCBwcmlvciwgaXQgaXMgY29tbW9uIHRvIHVzZQpub24tY2VudGVyZWQgcGFyYW1ldGVyaXphdGlvbiBmb3IgUkhTIHByaW9yLiBOb24tY2VudGVyZWQKcGFyYW1ldGVyaXphdGlvbiBpcyB1c2VmdWwgd2hlbiB0aGUgaW5mb3JtYXRpb24gZnJvbSB0aGUgbGlrZWxpaG9vZAppcyB3ZWFrIGFuZCB0aGUgcHJpb3IgZGVwZW5kZW5jeSBkb21pbmF0ZXMgaW4gdGhlIHBvc3RlcmlvcgpkZXBlbmRlbmN5LiBSSFMgaXMgb2Z0ZW4gdXNlZCB3aGVuIHRoZXJlIGFyZSBsZXNzIG9ic2VydmF0aW9ucyB0aGFuCnVua25vd25zLiBJbiB0aGlzIHByb2JsZW0gZWFjaCB1bmtub3duIChvbmUgZGF5IG9mIHllYXIgZWZmZWN0KSBpcwppbmZvcm1lZCBieSBzZXZlcmFsIG9ic2VydmF0aW9ucyBmcm9tIGRpZmZlcmVudCB5ZWFycywgYW5kIHRoZW4gaXQKbWlnaHQgYmUgdGhhdCB0aGUgY2VudGVyZWQgcGFyYW1ldGVyaXphdGlvbiBpcyBiZXR0ZXIuIEFuZCB0aGlzCnR1cm5lZCBvdXQgdG8gYmUgdHJ1ZSBhbmQgdGhlIGluZmVyZW5jZSBmb3IgbW9kZWwgOCB3aXRoIGNlbnRlcmVkCnBhcmFtZXRlcml6YXRpb24gUkhTIHByaW9yIG9uIGRheSBvZiB5ZWFyIGVmZmVjdCB3b3JrZWQgbXVjaCBiZXR0ZXIKdGhhbiBmb3IgbW9kZWwgNS4gIChJbiBTdGFuIGl0IHdhcyBlYXN5IHRvIHRlc3Qgc3dpdGNoIGZyb20Kbm9uLWNlbnRlcmVkIHRvIGNlbnRlcmVkIHBhcmFtZXRlcml6YXRpb24gYnkgcmVtb3ZpbmcgdGhlIG11bHRwbGllcgpmcm9tIG9uZSBvZiB0aGUgcGFyYW1ldGVyIGRlY2xhcmF0aW9ucykuCgpDb21waWxlIFN0YW4gbW9kZWwgOCArIFJIUyBbZ3BiZjhyaHMuc3Rhbl0oaHR0cHM6Ly9naXRodWIuY29tL2F2ZWh0YXJpL2Nhc2VzdHVkaWVzL2Jsb2IvbWFzdGVyL0JpcnRoZGF5cy9ncGJmOHJocy5zdGFuKQoKYGBge3IgbW9kZWw4cmhzLCByZXN1bHRzPSdoaWRlJ30KbW9kZWw4cmhzIDwtIGNtZHN0YW5fbW9kZWwoc3Rhbl9maWxlID0gcm9vdCgiQmlydGhkYXlzIiwgImdwYmY4cmhzLnN0YW4iKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5jbHVkZV9wYXRocyA9IHJvb3QoIkJpcnRoZGF5cyIpKQpgYGAKCkFkZCBhIGdsb2JhbCBzY2FsZSBmb3IgUkhTIHByaW9yCgpgYGB7ciB9CnN0YW5kYXRhOCA8LSBjKHN0YW5kYXRhOCwKICAgICAgICAgICAgICAgc2NhbGVfZ2xvYmFsPTAuMSkgIyBnbG9iYWwgc2NhbGUgZm9yIFJIUyBwcmlvcgpgYGAKCk9wdGltaXppbmcgaXMgZmFzdGVyIHRoYW4gc2FtcGxpbmcgKGFsdGhvdWdoIHRoaXMgcmVzdWx0IGNhbiBiZQp1c2VmdWwgaW4gYSBxdWljayB3b3JrZmxvdywgdGhlIHJlc3VsdCBzaG91bGQgbm90IGJlIHVzZWQgYXMgdGhlCmZpbmFsIHJlc3VsdCkuCgpgYGB7ciBvcHQ4cmhzLCByZXN1bHRzPSdoaWRlJ30Kb3B0OHJocyA8LSBtb2RlbDhyaHMkb3B0aW1pemUoZGF0YT1zdGFuZGF0YTgsIGluaXQ9MC4xLCBhbGdvcml0aG09J2xiZmdzJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGlzdG9yeT0xMDAsIHRvbF9vYmo9MTApCm9kcmF3czhyaHMgPC0gb3B0OHJocyRkcmF3cygpCmBgYAoKU2FtcGxlIHNob3J0IGNoYWlucyB1c2luZyB0aGUgb3B0aW1pemF0aW9uIHJlc3VsdCBhcyBpbml0aWFsIHZhbHVlcwooYWx0aG91Z2ggdGhlIHJlc3VsdCBmcm9tIHNob3J0IGNoYWlucyBjYW4gYmUgdXNlZnVsIGluIGEgcXVpY2sKd29ya2Zsb3csIHRoZSByZXN1bHQgc2hvdWxkIG5vdCBiZSB1c2VkIGFzIHRoZSBmaW5hbCByZXN1bHQpLgoKYGBge3IgfQppbml0OHJocyA8LSBzYXBwbHkoYygnbGVuZ3Roc2NhbGVfZjEnLCdsZW5ndGhzY2FsZV9mMicsJ2xlbmd0aHNjYWxlX2czJywKICAgICAgICAgICAgICAgICAgJ3NpZ21hX2YxJywnc2lnbWFfZjInLCdzaWdtYV9nMycsJ3NpZ21hX2Y0Jywnc2lnbWEnLAogICAgICAgICAgICAgICAgICAnYmV0YV9mMScsJ2JldGFfZjInLCdiZXRhX2YzJywnYmV0YV9nMycsJ2JldGFfZjQnLCdiZXRhX2Y1JywKICAgICAgICAgICAgICAgICAgJ3RhdV9mNCcsJ2xhbWJkYV9mNCcsJ2NhdXhfZjQnKSwKICAgICAgICAgICAgICAgIGZ1bmN0aW9uKHZhcmlhYmxlKSB7YXMubnVtZXJpYyhzdWJzZXQob2RyYXdzOHJocywgdmFyaWFibGU9dmFyaWFibGUpKX0pCmBgYApgYGB7ciBmaXQ4cmhzLCByZXN1bHRzPSdoaWRlJ30KZml0OHJocyA8LSBtb2RlbDhyaHMkc2FtcGxlKGRhdGE9c3RhbmRhdGE4LCBpdGVyX3dhcm11cD0xMDAsIGl0ZXJfc2FtcGxpbmc9MTAwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgY2hhaW5zPTQsIHBhcmFsbGVsX2NoYWlucz00LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5pdD1mdW5jdGlvbigpIHsgaW5pdDhyaHMgfSwgcmVmcmVzaD0xMCkKYGBgCgpDaGVjayB3aGV0aGVyIHBhcmFtZXRlcnMgaGF2ZSByZWFzb25hYmxlIHZhbHVlcwoKYGBge3IgfQpkcmF3czhyaHMgPC0gZml0OHJocyRkcmF3cygpCnN1bW1hcmlzZV9kcmF3cyhzdWJzZXQoZHJhd3M4cmhzLCB2YXJpYWJsZT1jKCdzaWdtYV8nLCdsZW5ndGhzY2FsZV8nLCdzaWdtYScsJ251XycpLCByZWdleD1UUlVFKSkKYGBgCgpDb21wYXJlIHRoZSBtb2RlbCB0byB0aGUgZGF0YQoKYGBge3IgfQpkcmF3czggPC0gYXNfZHJhd3NfbWF0cml4KGRyYXdzOHJocykKRWYgPC0gZXhwKGFwcGx5KHN1YnNldChkcmF3czgsIHZhcmlhYmxlPSdmJyksIDIsIG1lZGlhbikpCkVmMSA8LSBhcHBseShzdWJzZXQoZHJhd3M4LCB2YXJpYWJsZT0nZjEnKSwgMiwgbWVkaWFuKQpFZjEgPC0gZXhwKEVmMSAtIG1lYW4oRWYxKSArIG1lYW4obG9nKGRhdGEkYmlydGhzX3JlbGF0aXZlMTAwKSkpCkVmMiA8LSBhcHBseShzdWJzZXQoZHJhd3M4LCB2YXJpYWJsZT0nZjInKSwgMiwgbWVkaWFuKQpFZjIgPC0gZXhwKEVmMiAtIG1lYW4oRWYyKSArIG1lYW4obG9nKGRhdGEkYmlydGhzX3JlbGF0aXZlMTAwKSkpCkVmX2RheV9vZl93ZWVrIDwtIGFwcGx5KHN1YnNldChkcmF3czgsIHZhcmlhYmxlPSdmX2RheV9vZl93ZWVrJyksIDIsIG1lZGlhbikKRWZfZGF5X29mX3dlZWsgPC0gZXhwKEVmX2RheV9vZl93ZWVrIC0gbWVhbihFZl9kYXlfb2Zfd2VlaykgKyBtZWFuKGxvZyhkYXRhJGJpcnRoc19yZWxhdGl2ZTEwMCkpKQpFZjMgPC0gYXBwbHkoc3Vic2V0KGRyYXdzOCwgdmFyaWFibGU9J2YzJyksIDIsIG1lZGlhbikKRWYzIDwtIGV4cChFZjMgLSBtZWFuKEVmMykgKyBtZWFuKGxvZyhkYXRhJGJpcnRoc19yZWxhdGl2ZTEwMCkpKQpFZjQgPC0gYXBwbHkoc3Vic2V0KGRyYXdzOCwgdmFyaWFibGU9J2JldGFfZjQnKSwgMiwgbWVkaWFuKSpzZChsb2coZGF0YSRiaXJ0aHNfcmVsYXRpdmUxMDApKQpFZjQgPC0gZXhwKEVmNCkqMTAwCkVmbG9hdHMgPC0gYXBwbHkoc3Vic2V0KGRyYXdzOCwgdmFyaWFibGU9J2JldGFfZjUnKSwgMiwgbWVkaWFuKSpzZChsb2coZGF0YSRiaXJ0aHNfcmVsYXRpdmUxMDApKQpFZmxvYXRzIDwtIGV4cChFZmxvYXRzKSoxMDAKZmxvYXRzMTk4ODwtYyhtZW1vcmlhbF9kYXlzWzIwXSwgbGFib3JfZGF5c1tjKDIwLDQwKV0sIHRoYW5rc2dpdmluZ19kYXlzW2MoMjAsNDApXSktNjkzOQpFZjRmbG9hdCA8LSBFZjQKRWY0ZmxvYXRbZmxvYXRzMTk4OF0gPC0gRWY0ZmxvYXRbZmxvYXRzMTk4OF0qRWZsb2F0c1tjKDEsMiwyLDMsMyldLzEwMApwZiA8LSBkYXRhICU+JQogIG11dGF0ZShFZiA9IEVmKSAlPiUKICBnZ3Bsb3QoYWVzKHg9ZGF0ZSwgeT1iaXJ0aHNfcmVsYXRpdmUxMDApKSArIGdlb21fcG9pbnQoY29sb3I9c2V0MVsyXSwgYWxwaGE9MC4yKSArCiAgZ2VvbV9wb2ludChhZXMoeT1FZiksIGNvbG9yPXNldDFbMV0sIGFscGhhPTAuMikgKwogIGxhYnMoeD0iRGF0ZSIsIHk9IlJlbGF0aXZlIG51bWJlciBvZiBiaXJ0aHMiKQpwZjEgPC0gZGF0YSAlPiUKICBtdXRhdGUoRWYxID0gRWYxKSAlPiUKICBnZ3Bsb3QoYWVzKHg9ZGF0ZSwgeT1iaXJ0aHNfcmVsYXRpdmUxMDApKSArIGdlb21fcG9pbnQoY29sb3I9c2V0MVsyXSwgYWxwaGE9MC4yKSArCiAgZ2VvbV9saW5lKGFlcyh5PUVmMSksIGNvbG9yPXNldDFbMV0pICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9MTAwLCBjb2xvcj0nZ3JheScpICsKICBsYWJzKHg9IkRhdGUiLCB5PSJSZWxhdGl2ZSBudW1iZXIgb2YgYmlydGhzIikKcGYyIDwtIGRhdGEgJT4lCiAgbXV0YXRlKEVmMiA9IEVmMikgJT4lCiAgZ3JvdXBfYnkoZGF5X29mX3llYXIyKSAlPiUKICBzdW1tYXJpc2UobWVhbmJpcnRocz1tZWFuKGJpcnRoc19yZWxhdGl2ZTEwMCksIG1lYW5FZjI9bWVhbihFZjIpKSAlPiUKICBnZ3Bsb3QoYWVzKHg9YXMuRGF0ZSgiMTk4Ny0xMi0zMSIpK2RheV9vZl95ZWFyMiwgeT1tZWFuYmlydGhzKSkgKyBnZW9tX3BvaW50KGNvbG9yPXNldDFbMl0sIGFscGhhPTAuMikgKwogIHNjYWxlX3hfZGF0ZShkYXRlX2JyZWFrcyA9ICIxIG1vbnRoIiwgZGF0ZV9sYWJlbHMgPSAiJWIiKSArCiAgZ2VvbV9saW5lKGFlcyh5PW1lYW5FZjIpLCBjb2xvcj1zZXQxWzFdKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PTEwMCwgY29sb3I9J2dyYXknKSArCiAgbGFicyh4PSJEYXRlIiwgeT0iUmVsYXRpdmUgbnVtYmVyIG9mIGJpcnRocyBvZiB5ZWFyIikKcGYzIDwtIGdncGxvdChkYXRhPWRhdGEsIGFlcyh4PWRheV9vZl93ZWVrLCB5PWJpcnRoc19yZWxhdGl2ZTEwMCkpICsgZ2VvbV9wb2ludChjb2xvcj1zZXQxWzJdLCBhbHBoYT0wLjIpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gMTo3LCBsYWJlbHM9YygnTW9uJywnVHVlJywnV2VkJywnVGh1JywnRnJpJywnU2F0JywnU3VuJykpICsKICBnZW9tX2xpbmUoZGF0YT1kYXRhLmZyYW1lKHg9MTo3LHk9RWZfZGF5X29mX3dlZWspLCBhZXMoeD14LCB5PUVmX2RheV9vZl93ZWVrKSwgY29sb3I9c2V0MVsxXSkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdD0xMDAsIGNvbG9yPSdncmF5JykgKwogIGxhYnMoeD0iRGF0ZSIsIHk9IlJlbGF0aXZlIG51bWJlciBvZiBiaXJ0aHMgb2Ygd2VlayIpCk49bGVuZ3RoKGRhdGEkaWQpCnBmM2IgPC0gZGF0YSAlPiUKICBtdXRhdGUoRWYzID0gRWYzKkVmMS8xMDApICU+JQogIGdncGxvdChhZXMoeD1kYXRlLCB5PWJpcnRoc19yZWxhdGl2ZTEwMCkpICsgZ2VvbV9wb2ludChjb2xvcj1zZXQxWzJdLCBhbHBoYT0wLjIpICsKICBnZW9tX3BvaW50KGFlcyh5PUVmMyksIGNvbG9yPXNldDFbMV0sIHNpemU9MC4xKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PTEwMCwgY29sb3I9J2dyYXknKSArCiAgbGFicyh4PSJEYXRlIiwgeT0iUmVsYXRpdmUgbnVtYmVyIG9mIGJpcnRocyIpICsKICBhbm5vdGF0ZSgidGV4dCIseD1hcy5EYXRlKCIxOTg5LTA4LTAxIikseT0oRWYzKkVmMS8xMDApW2MoKE4tNSk6KE4tNCksIE4sIE4tNildLGxhYmVsPWMoIk1vbiIsIlR1ZSIsIlNhdCIsIlN1biIpKQpmMTMgPC0gZGF0YSAlPiUgZmlsdGVyKHllYXI9PTE5ODgpJT4lc2VsZWN0KGRheSxkYXRlKSU+JW11dGF0ZSh5PUVmNGZsb2F0KSU+JWZpbHRlcihkYXk9PTEzKQoKcGYyYiA8LWRhdGEuZnJhbWUoeD1hcy5EYXRlKCIxOTg4LTAxLTAxIikrMDozNjUsIHk9RWY0ZmxvYXQpICU+JQogIGdncGxvdChhZXMoeD14LHk9eSkpICsgZ2VvbV9saW5lKGNvbG9yPXNldDFbMV0pICsKICBzY2FsZV94X2RhdGUoZGF0ZV9icmVha3MgPSAiMSBtb250aCIsIGRhdGVfbGFiZWxzID0gIiViIikgKwogIGdlb21faGxpbmUoeWludGVyY2VwdD0xMDAsIGNvbG9yPSdncmF5JykgKwogIGxhYnMoeD0iRGF0ZSIsIHk9IlJlbGF0aXZlIG51bWJlciBvZiBiaXJ0aHMgb2YgeWVhciIpICsKICBhbm5vdGF0ZSgidGV4dCIseD1hcy5EYXRlKCIxOTg4LTAxLTAxIikseT1FZjRmbG9hdFsxXS0xLGxhYmVsPSJOZXcgeWVhciIpICsKICBhbm5vdGF0ZSgidGV4dCIseD1hcy5EYXRlKCIxOTg4LTAyLTE0IikseT1FZjRmbG9hdFs0NV0rMS41LGxhYmVsPSJWYWxlbnRpbmUncyBkYXkiKSArCiAgYW5ub3RhdGUoInRleHQiLHg9YXMuRGF0ZSgiMTk4OC0wMi0yOSIpLHk9RWY0ZmxvYXRbNjBdLTIuNSxsYWJlbD0iTGVhcCBkYXkiKSArCiAgYW5ub3RhdGUoInRleHQiLHg9YXMuRGF0ZSgiMTk4OC0wNC0wMSIpLHk9RWY0ZmxvYXRbOTJdLTEuNSxsYWJlbD0iQXByaWwgMXN0IikgKyAKICBhbm5vdGF0ZSgidGV4dCIseD1hcy5EYXRlKCIxOTg4LTA3LTA0IikseT1FZjRmbG9hdFsxODZdLTEuNSxsYWJlbD0iSW5kZXBlbmRlbmNlIGRheSIpICsKICBhbm5vdGF0ZSgidGV4dCIseD1hcy5EYXRlKCIxOTg4LTEwLTMxIikseT1FZjRmbG9hdFszMDVdLTEuNSxsYWJlbD0iSGFsbG93ZWVuIikgKyAKICBhbm5vdGF0ZSgidGV4dCIseD1hcy5EYXRlKCIxOTg4LTEyLTI0IikseT1FZjRmbG9hdFszNjBdLTIsbGFiZWw9IkNocmlzdG1hcyIpICsKICBhbm5vdGF0ZSgidGV4dCIseD1hcy5EYXRlKCIxOTg4LTA1LTMwIikseT1FZjRmbG9hdFsxNTFdLTIsbGFiZWw9Ik1lbW9yaWFsIGRheSIpICsKICBhbm5vdGF0ZSgidGV4dCIseD1hcy5EYXRlKCIxOTg4LTA5LTA1IikseT1FZjRmbG9hdFsyNDldLTEuNSxsYWJlbD0iTGFib3IgZGF5IikgKyAKICBhbm5vdGF0ZSgidGV4dCIseD1hcy5EYXRlKCIxOTg4LTExLTI0IikseT1FZjRmbG9hdFszMjldLTEsbGFiZWw9IlRoYW5rc2dpdmluZyIpKwogIGdlb21fcG9pbnQoZGF0YT1mMTMsYWVzKHg9ZGF0ZSx5PXkpLCBzaXplPTMsIHNoYXBlPTEpCihwZiArIHBmMSkgLyAocGYyICsgcGYzYikgLyAocGYyYikKYGBgCgpWaXN1YWxseSB3ZSBnZXQgcXVpdGUgc2ltaWxhciByZXN1bHQgYXMgd2l0aCAkdF9cbnUkIHByaW9yLiBXaGVuIHdlCmNvbXBhcmUgdGhlIG1vZGVscyB3aXRoIExPTy1DViwgdGhlcmUgaXMgbm90IG11Y2ggZGlmZmVyZW5jZQpiZXR3ZWVuIHRoZXNlIHByaW9ycy4KCmBgYHtyIH0KbG9vOHJoczwtZml0OHJocyRsb28oKQpsb29fY29tcGFyZShsaXN0KGBNb2RlbCA4IFN0dWRlbnRzIHRgPWxvbzh0bnUsYE1vZGVsIDggUkhTYD1sb284cmhzKSkKYGBgCgojIyMgRnVydGhlciBpbXByb3ZlbWVudHMgZm9yIHRoZSBkYXkgb2YgeWVhciBlZmZlY3QKCkl0J3MgdW5saWtlbHkgdGhhdCBkYXkgb2YgeWVhciBlZmZlY3Qgd291bGQgYmUgdW5zdHJ1Y3R1cmVkIHdpdGgKc29tZSBkaXN0cmlidXRpb24gbGlrZSBSSFMsIGFuZCB0aHVzIGluc3RlYWQgb2YgdHJ5aW5nIHRvIGZpbmQgYQpwcmlvciBkaXN0cmlidXRpb24gdGhhdCB3b3VsZCBpbXByb3ZlIExPTy1DViwgaXQgd291bGQgbWFrZSBtb3JlCnNlbnNlIHRvIGZ1cnRoZXIgYWRkIHN0cnVjdHVyYWwgaW5mb3JtYXRpb24uIEZvciBleGFtcGxlLCBpdCB3b3VsZApiZSBwb3NzaWJsZSB0byBhZGQgbW9yZSBrbm93biBzcGVjaWFsIGRheXMgYW5kIHRha2UgaW50byBhY2NvdW50CnRoYXQgYSBzcGVjaWFsIGRheSBlZmZlY3QgYW5kIHdlZWtlbmQgZWZmZWN0IHByb2JhYmx5IGFyZSBub3QKYWRkaXRpdmUuIEZ1cnRoZXJtb3JlIGlmIHRoZXJlIGFyZSBsZXNzIGJpcnRocyBkdXJpbmcgc29tZSBkYXksIHRoZQpiaXJ0aHMgbmVlZCB0byBoYXBwZW4gc29tZSBvdGhlciBkYXkgYW5kIGl0IGNhbiBiZSBhc3N1bWVkIHRoYXQKdGhlcmUgd291bGQgYmUgY29ycmVzcG9uZGluZyBleGNlc3Mgb2YgYmlydGhzIGJlZm9yZSBvZiBhZnRlciBhCmJhbmsgaG9saWRheS4gVGhpcyByaW5naW5nIGFyb3VuZCBkYXlzIHdpdGggbGVzcyBiaXJ0aHMgaXMgbm90CnNpbXBsZSBhcyBpdCBpcyBhbHNvIGFmZmVjdGVkIHdoZXRoZXIgdGhlIHByZXZpb3VzIGFuZCBmb2xsb3dpbmcKZGF5cyBhcmUgd2Vla2VuZCBkYXlzLiBUaGlzIGFsbCBnZXRzIG1vcmUgY29tcGxpY2F0ZWQgdGhhbiB3ZSB3YW50CnRvIGluY2x1ZGUgaW4gdGhpcyBjYXNlIHN0dWR5LCBidXQgdGhlIHJlYWRlciBjYW4gc2VlIGhvdyB0aGUKc2ltaWxhciBncmFkdWFsIG1vZGVsIGJ1aWxkaW5nIGNvdWxkIGJlIG1hZGUgYnkgYWRkaW5nIGFkZGl0aW9uYWwKY29tcG9uZW50cy4gRXZlbnR1YWxseSBpdCBpcyBsaWtlbHkgdGhhdCB0aGVyZSBzdGFydHMgdG8gYmUgd29ycnkKb2Ygb3ZlcmZpdHRpbmcsIGJ1dCBpbnRlZ3JhdGlvbiBvdmVyIHRoZSB1bmtub3duIGFsbGV2aWF0ZXMgdGhhdAphbmQgbG9va2luZyBhdCB0aGUgcHJlZGljdGl2ZSBwZXJmb3JtYW5jZSBlc3RpbWF0ZXMgc3VjaCBMT08tQ1YgY2FuCmhlbHAgdG8gZGVjaWRlIHdoZW4gdGhlIGFkZGl0aW9uYWwgbW9kZWwgY29tcG9uZW50cyBkb24ndCBpbXByb3ZlCnRoZSBwcmVkaWN0aXZlIHBlcmZvcm1hbmNlIG9yIGNhbid0IGJlIHdlbGwgaWRlbnRpZmllZC4KCiMjIyBRdWFudGl0YXRpdmUgcHJlZGljdGl2ZSBwZXJmb3JtYW5jZSBmb3IgdGhlIHNlcmllcyBvZiBtb2RlbHMKCldlIGRpZG4ndCB1c2UgTE9PLUNWIHVudGlsIGluIHRoZSBlbmQsIGFzIHRoZSBxdWFsaXRhdGl2ZQpkaWZmZXJlbmNlcyBiZXR3ZWVuIG1vZGVscyB3ZXJlIHZlcnkgY29udmluY2luZy4gV2UgY2FuIHVzZSBMT08tQ1YKdG8gY2hlY2sgaG93IGJpZyB0aGUgZGlmZmVyZW5jZSBpbiB0aGUgcHJlZGljdGl2ZSBwZXJmb3JtYW5jZSBhcmUKYW5kIGlmIHRoZSBkaWZmZXJlbmNlcyBhcmUgYmlnLCB3ZSBrbm93IHRoYXQgbW9kZWwgYXZlcmFnaW5nIHRoYXQKd291bGQgdGFrZSBpbnRvIGFjY291bnQgdGhlIHVuY2VydGFpbnR5IHdvdWxkIGdpdmUgd2VpZ2h0cyBjbG9zZSB0bwp6ZXJvIGZvciBhbGwgYnV0IHRoZSBtb3N0IGVsYWJvcmF0ZSBtb2RlbHMuCgpgYGB7ciB9CmxvbzE8LWZpdDEkbG9vKCkKbG9vMjwtZml0MiRsb28oKQpsb28zPC1maXQzJGxvbygpCmxvbzQ8LWZpdDQkbG9vKCkKbG9vNjwtZml0NiRsb28oKQpsb283PC1maXQ3JGxvbygpCmxvb19jb21wYXJlKGxpc3QoYE1vZGVsIDFgPWxvbzEsYE1vZGVsIDJgPWxvbzIsYE1vZGVsIDNgPWxvbzMsYE1vZGVsIDRgPWxvbzQsYE1vZGVsIDZgPWxvbzYsYE1vZGVsIDdgPWxvbzcsYE1vZGVsIDggKyB0X251YD1sb284dG51KSkKYGBgCgojIyMgUmVzaWR1YWwgYW5hbHlzaXMKCldlIGNhbiBnZXQgZnVydGhlciBpZGVhcyBmb3IgaG93IHRvIGltcHJvdmUgdGhlIG1vZGVsIGFsc28gYnkKbG9va2luZyBhdCB0aGUgcmVzaWR1YWxzLgoKYGBge3IgfQpkcmF3czggPC0gYXNfZHJhd3NfbWF0cml4KGRyYXdzOHRudSkKRWYgPC0gZXhwKGFwcGx5KHN1YnNldChkcmF3czgsIHZhcmlhYmxlPSdmJyksIDIsIG1lZGlhbikpCmRhdGEgJT4lCiAgbXV0YXRlKEVmID0gRWYpICU+JQogIGdncGxvdChhZXMoeD1kYXRlLCB5PWxvZyhiaXJ0aHNfcmVsYXRpdmUxMDAvRWYpKSkgKyBnZW9tX3BvaW50KGNvbG9yPXNldDFbMl0pICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9MCwgY29sb3I9J2dyYXknKSArCiAgc2NhbGVfeF9kYXRlKGRhdGVfYnJlYWtzID0gIjEgeWVhciIsIGRhdGVfbGFiZWxzID0gIiVZIikgKwogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IueD1lbGVtZW50X2xpbmUoY29sb3I9J2dyYXknLHNpemU9MSkpCmBgYAoKV2UgY2FuIHNlZSBzb21lIHN0cnVjdHVyZSwgc3BlY2lmaWNhbGx5IGluIHllYXJzIDE5NjktLTE5NzggdGhlCnJlc2lkdWFsIGhhcyBuZWdhdGl2ZSBwZWFrIGluIHRoZSBtaWRkbGUgb2YgdGhlIHllYXIsIHdoaWxlIGluIHllYXJzCjE5ODEtLTE5ODggdGhlIHJlc2lkdWFsIGhhcyBwb3NpdGl2ZSBwZWFrIGluIHRoZSBtaWRkbGUgb2YgdGhlCnllYXIuIFRoaXMga2luZCBvZiBwYXR0ZXJuIGFwcGVhcnMgYXMgd2UgdXNlIHRoZSBzYW1lIHNlYXNvbmFsCmVmZmVjdCBmb3IgYWxsIHllYXJzLCBidXQgdGhlIG1hZ25pdHVkZSBvZiBzZWFzb25hbCBlZmZlY3QgaXMKY2hhbmdpbmcuIEl0IHdvdWxkIGJlIHBvc3NpYmxlIHRvIG1vZGlmeSB0aGUgbW9kZWwgdG8gaW5jbHVkZQpncmFkdWFsbHkgY2hhbmdpbmcgc2Vhc29uYWwgZWZmZWN0LCBidXQgbGVhdmUgaXQgb3V0IGZyb20gdGhpcyBjYXNlCnN0dWR5LiAKClRoZSBiZXN0IG1vZGVsIHNvIGZhciBleHBsYWlucyBhbHJlYWR5IDk0JSBvZiB0aGUgdmFyaWFuY2UgKExPTy1SMikuCgpgYGB7ciB9CmRyYXdzOCA8LSBhc19kcmF3c19tYXRyaXgoZHJhd3M4dG51KQpmIDwtIGV4cChzdWJzZXQoZHJhd3M4LCB2YXJpYWJsZT0nZicpKQpsb284dG51IDwtIGZpdDh0bnUkbG9vKHNhdmVfcHNpcz1UUlVFKQpFZmxvbyA8LSBFX2xvbyhmLCBwc2lzX29iamVjdD1sb284dG51JHBzaXNfb2JqZWN0KSR2YWx1ZQpMT09SMiA8LSAxLXZhcihsb2coZGF0YSRiaXJ0aHNfcmVsYXRpdmUxMDAvRWZsb28pKS92YXIobG9nKGRhdGEkYmlydGhzX3JlbGF0aXZlMTAwKSkKcHJpbnQoTE9PUjIsIGRpZ2l0cz0yKQpgYGAKCkFzIGl0IHNlZW1zIHdlIGNvdWxkIHN0aWxsIGltcHJvdmUgYnkgYWRkaW5nIG1vcmUgc3RydWN0dXJlIGFuZAp0aW1lIHZhcnlpbmcgc2Vhc29uYWwgZWZmZWN0LCBpdCBzZWVtcyB0aGUgdmFyaWFiaWxpdHkgaW4gdGhlCm51bWJlciBvZiBiaXJ0aHMgZnJvbSBkYXkgdG8gZGF5IGlzIHF1aXRlIHdlbGwgcHJlZGljdGFibGUuIE9mCmNvdXJzZSBiaWcgcGFydCBvZiB0aGUgdmFyaWF0aW9uIGlzIGR1ZSB0byBwbGFubmVkIGluZHVjZWQgYmlydGhzCmFuZCBjLXNlY3Rpb25zLCBhbmQgdGh1cyBob3NwaXRhbHMgZG8gYWxyZWFkeSBjb250cm9sIHRoZSBudW1iZXIgb2YKYmlydGhzIHBlciBkYXkgYW5kIHRoZXJlIGlzIG5vIHJlYWxseSBwcmFjdGljYWwgdXNlIGZvciB0aGUKcmVzdWx0LiBIb3dldmVyIHRoZXJlIGFyZSBwbGVudHkgb2Ygc2ltaWxhciB0aW1lIHNlcmllcywgZm9yCmV4YW1wbGUsIGluIGNvbnN1bWVyIGJlaGF2aW9yIHRoYXQgYXJlIGFmZmVjdGVkIGJ5IHNwZWNpYWwgZGF5cy4KCiMjIyBNb3JlIGFjY3VyYXRlIGluZmVyZW5jZQoKRHVyaW5nIGFsbCB0aGUgaXRlcmF0aXZlIG1vZGVsIGJ1aWxkaW5nIHdlIGZhdm9yZWQgb3B0aW1pemF0aW9uIGFuZApzaG9ydCBNQ01DIGNoYWlucy4gSW4gdGhlIGVuZCB3ZSBhbHNvIHJ1biB3aXRoIGhpZ2hlciBhZGFwdF9kZWx0YQp0byByZWR1Y2UgdGhlIHByb2JhYmlsaXR5IG9mIGRpdmVyZ2VuY2VzLCBoaWdoZXIgbWF4aW11bSB0cmVlZGVwdGgKdG8gZW5zdXJlIGhpZ2hlciBlZmZlY3RpdmUgc2FtcGxlIHNpemUgcGVyIGl0ZXJhdGlvbiAoRVNTIHBlcgpzZWNvbmQgZG9lc24ndCBuZWNlc3NhcmlseSBpbXByb3ZlKSwgYW5kIHJ1biBtdWNoIGxvbmdlciBjaGFpbnMsCmJ1dCBkaWRuJ3Qgc2VlIHByYWN0aWNhbCBkaWZmZXJlbmNlcyBpbiBwbG90cyBvciBMT08tQ1YgdmFsdWVzLiBBcwpydW5uaW5nIHRoZXNlIGxvbmdlciBjaGFpbnMgY2FuIHRha2UgaG91cnMgdGhleSBhcmUgbm90IHJ1biBhcyBwYXJ0Cm9mIHRoaXMgbm90ZWJvb2suIEFuIGV4YW1wbGUgb2YgaG93IHRvIHJlZHVjZSBwcm9iYWJpbGl0eSBvZgpkaXZlcmdlbmNlcyBhbmQgaW5jcmVhc2UgbWF4aW11bSB0cmVlZGVwdGggaXMgc2hvd24gYmVsb3cgKHRoZXJlIGlzCnJhcmVseSBuZWVkIHRvIGluY3JlYXNlIGFkYXB0X2RlbHRhIGxhcmdlciB0aGFuIDAuOTUgYW5kIGlmIHRoZXJlCmFyZSBzdGlsbCBkaXZlcmdlbmNlcyB3aXRoIGFkYXB0X2RlbHRhIGVxdWFsIHRvIDAuOTksIHRoZSBwb3N0ZXJpb3IKaGFzIHNlcmlvdXMgcHJvYmxlbXMgYW5kIGl0IHNob3VsZCBiZSBjb25zaWRlcmVkIHdoZXRoZXIKcmUtcGFyYW1ldGVyaXphdGlvbiwgYmV0dGVyIGRhdGEgb3IgbW9yZSBpbmZvcm1hdGl2ZSBwcmlvcnMgY291bGQKaGVscCkuCgpgYGB7ciB9CiMjIGZpdDh0bnUgPC0gbW9kZWw4dG51JHNhbXBsZShkYXRhPXN0YW5kYXRhOCwgY2hhaW5zPTQsIHBhcmFsbGVsX2NoYWlucz00LAojIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWRhcHRfZGVsdGE9MC45NSwgbWF4X3RyZWVkZXB0aD0xNSkKYGBgCgo=