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.

Stan model codes are available in the corresponding git repo


Load packages

library("rprojroot")
root<-has_file(".Workflow-Examples-root")$make_fix_file()
library(tidyverse)
library(cmdstanr)
library(posterior)
options(pillar.neg = FALSE, pillar.subtle=FALSE, pillar.sigfig=2)
library(loo)
library(bayesplot)
theme_set(bayesplot::theme_default(base_family = "sans"))
library(patchwork)
set1 <- RColorBrewer::brewer.pal(7, "Set1")

Use English for names of weekdays and months

Sys.setlocale("LC_TIME", "en_GB.UTF-8")
[1] "en_GB.UTF-8"

Load and plot data

Load birthdays per day in USA 1969-1988:

data <- read_csv(root("Birthdays/data", "births_usa_1969.csv"))

Add date type column for plotting

data <- data %>%
  mutate(date = as.Date("1968-12-31") + id,
         births_relative100 = births/mean(births)*100)

Plot all births

We can see slow variation in trend, yearly pattern, and especially in the later years spread to lower and higher values.

data %>%
  ggplot(aes(x=date, y=births)) + geom_point(color=set1[2]) +
  labs(x="Date", y="Relative number of births")

Plot all births as relative to mean

To make the interpretation we switch to examine the relative change, with the mean level denoted with 100.

data %>%
  ggplot(aes(x=date, y=births_relative100)) + geom_point(color=set1[2]) +
  geom_hline(yintercept=100, color='gray') +
  labs(x="Date", y="Relative births per day")

Plot mean per day of year

We can see the generic pattern in yearly seasonal trend simply by averaging over each day of year (day_of_year has numbers from 1 to 366 every year with leap day being 60 and 1st March 61 also on non-leap-years).

data %>%
  group_by(day_of_year2) %>%
  summarise(meanbirths=mean(births_relative100)) %>%
  ggplot(aes(x=as.Date("1986-12-31")+day_of_year2, y=meanbirths)) +
  geom_point(color=set1[2]) +
  geom_hline(yintercept=100, color='gray') +
  scale_x_date(date_breaks = "1 month", date_labels = "%b") +
  labs(x="Day of year", y="Relative births per day of year")

Plot mean per day of week

We can see the generic pattern in weekly trend simply by averaging over each day of week.

data %>%
  group_by(day_of_week) %>%
  summarise(meanbirths=mean(births_relative100)) %>%
  ggplot(aes(x=day_of_week, y=meanbirths)) +
  geom_point(color=set1[2], size=4) +
  geom_hline(yintercept=100, color='gray') +
  scale_x_continuous(breaks = 1:7, labels=c('Mon','Tue','Wed','Thu','Fri','Sat','Sun')) +
  labs(x="Day of week", y="Relative number of births of week")

Previous analyses

We have analysed the same data before in BDA3 and thus had idea of what kind of model to use. For BDA3 we used GPstuff software which is Gaussian process specific software for Matlab and Octave. As Stan has aimed to be very generic it can be slower than specialized software for some specific models such as Gaussian processes, but Stan provides more flexibility in the model definition.

Riutort-Mayol et al (2020) demonstrate Hilbert space approximate basis function approximation of Gaussian processes also for the same birthday data. In the experiments the inference was slower than expected raising suspicion of inefficient model code or bad posterior shape due to bad model specification.

Workflow for quick iterative model building

Even we have general idea for the model (slow trend, seasonal trend, weekday effect, etc), adding them all at once to the model makes the model complex and difficult to debug and solve the computational problems. It is thus natural to build the model gradually and check that each addition works before adding the next model component. During this iterative model building we want the inference to be fast, but it doesn’t need to be very accurate as long as qualitatively the new model is reasonable. For quick testing and iterative model building we can use optimization and shorter MCMC chains that we would not recommend for the final inference. Furthermore, in this specific example, the new additions are qualitatively so clear improvements that there is no need for quantitative model comparison whether the additions are ``significant’’ (see also Navarro, 2019) and there is no danger of overfitting. Although there is one part of the model where the data is weakly informative and the prior choices seem to matter and we’ll get back to this and consequences later. Overall we build tens of different models, but illustrate here only the main line.

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.

Quantitative predictive performance for the series of models

We didn’t use LOO-CV until in the end, as the qualitative differences between models were very convincing. We can use LOO-CV to check how big the difference in the predictive performance are and if the differences are big, we know that model averaging that would take into account the uncertainty would give weights close to zero for all but the most elaborate models.

loo1<-fit1$loo()
loo2<-fit2$loo()
loo3<-fit3$loo()
loo4<-fit4$loo()
loo6<-fit6$loo()
loo7<-fit7$loo()
loo_compare(list(`Model 1`=loo1,`Model 2`=loo2,`Model 3`=loo3,`Model 4`=loo4,`Model 6`=loo6,`Model 7`=loo7,`Model 8 + t_nu`=loo8tnu))
               elpd_diff se_diff
Model 8 + t_nu     0.0       0.0
Model 7         -960.3      47.6
Model 6        -1565.3      89.6
Model 4        -1993.8     129.2
Model 3        -2478.5     115.3
Model 2        -8488.9     101.8
Model 1        -9033.4     103.0

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=