Post script Appendix

This notebook is a post script appendix to the paper

  • Aki Vehtari, Andrew Gelman, Daniel Simpson, Bob Carpenter, Paul-Christian Bürkner (2021): Rank-normalization, folding, and localization: An improved \(\widehat{R}\) for assessing convergence of MCMC. Bayesian analysis, 16(2):667-718. doi:10.1214/20-BA1221.

The code used to make this notebook is available at https://github.com/avehtari/rhat_ess.

Introduction

The MCMC effective sample size (ESS) and Monte Carlo standard error (MCSE) estimated for one chain includes estimation of the correlation between the iterations, for example, using autocorrelation time (or spectral density at frequency zero). As a finite number of MCMC draws are available the autocorrelation estimates for bigger lags are noisier. In the paper, we wrote about autocorrelation estimators

In our experiments, Geyer’s [(1992)] truncation had superior stability compared to flat-top (Doss et al., 2014) and slug-sail (Vats and Knudson, 2018) lag window approaches.

(Vats and Knudson (2018) has since been published as Vats and Knudson (2021)). As the main point of the paper was not comparison of autocorrelation estimators, the experimental results were not included in the paper.

This notebook includes additional experiments comparing the Geyer’s (1992) truncation approach to three other methods for estimating effective sample size (ESS) and corresponding Monte Carlo standard error (MCSE). We also compare the new quantile MCSE method we proposed in the paper (Vehtari et al., 2021) to batch means quantile MCSE method by (Gong and Flegal, 2015). Finally, we compare the behavior of ESS implementations in four R packages in case of multiple chains and well separated multimodal distribution.

The methods and R packages compared

  • stableGR (n.eff) computes a weighted sum of autocorrelations using slug-sail lag window (Vats and Knudson, 2018)
  • ‘mcmcse’ (ess and mcse.q) uses by default a batch means approach (Gong and Flegal, 2015)
  • coda (effectiveSize) computes the spectral density at frequency zero by fitting an AR model to the chain (Heidelberger and Welch, 1981)
  • posterior: ess_basic computes sum of autocorrelations using Geyer’s truncation rule (Geyer, 1992), and mcse_quantile uses in addition an inverse approach for quantile MCSE. The posterior package also takes into account the the possibility that the chains are not mixing by using also multi-chain \(\widehat{R}\) in ESS and MCSE computations.

tl;dr

In case of well mixing chains, ess_basic and mcse_quantile in the posterior package are the most accurate methods. In case of well separated modes, the posterior package also provides appropriately small ESS indicating that the chains are not mixing.

Comparison of ESS estimators with AR(1) simulation

Mean of x

We simulate \(M=4\) chains from a known AR(1) process with varying parameter \(\phi\), and normal(0,1) marginal distribution.

x1 = arima.sim(n = N, list(ar = c(phi)), sd = sqrt((1-phi^2)));
x2 = arima.sim(n = N, list(ar = c(phi)), sd = sqrt((1-phi^2)));
x3 = arima.sim(n = N, list(ar = c(phi)), sd = sqrt((1-phi^2)));
x4 = arima.sim(n = N, list(ar = c(phi)), sd = sqrt((1-phi^2)));

We vary the length of the chains, \(N\), and the AR process parameter \(\phi\)

Ns=c(100, 1000, 10000)
phis=c(-0.3, -0.1, 0.1, 0.3, 0.5, 0.7, 0.9)

\(N=1000\) is the default in Stan, and \(N=100\) and \(N=10000\) are used to illustrate the behavior with less or more MCMC iterations. The total number of draws is \(S=M*N\).

The values of \(\phi\) have been chosen to reflect typical ESS/\(S\) values seen from Stan. The chosen \(\phi\) values correspond to ESS/\(S\) values \((1.9, 1.2, 0.8, 0.5, 0.3, 0.2, 0.1)\). The two first \(\phi\) values being negative produce antithetic chains with ESS>\(S\), which is not rare when using Stan’s dynamic Hamiltonian Monte Carlo.

Simulation is repeated \(100\,000\) times for each combination of \(N\) and \(\phi\). For each simulation we compute the empirical mean. The variance of the empirical means and known true variance are used to compute the asymptotic efficiency, that is, true ESS/\(S\) when \(S\rightarrow \infty\). For each simulation we compute three ESS estimates and use those to compute Monte Carlo standard error (MCSE) which is compared to the actual error, that is, the difference between the empirical mean \(\hat{\mu}\) and known true mean \(\mu=0\).

The following figure summarizes the results. In the case of estimating some posterior expectation \(\mathrm{E}[g(x)]\), the used ESS estimators assume finite variance and MCSE is simply \(\mathrm{sd}[g(x)]/\sqrt{\mathrm{ESS}}\). Thus accuracy of ESS is directly related to the accuracy of MCSE. We report root mean square of standardized errors \(|\mu - \hat{\mu}|/\widehat{\mathrm{MCSE}}\), which is in case of well calibrated ESS and MCSE should be close to 1. Values less than 1 indicate underestimated ESS and overestimated MCSE, and values larger than 1 indicate overestimated ESS and underestimated MCSE.

posterior: Geyer is on average clearly closer to 1 than the other methods. coda: AR method is overconfident with short chains, but has good behavior with longer chains. stableGR: Slug-sail has quite unpredictable behavior being either over- or underconfident, except in case of very long chains when the method is consistently overconfident for antithetic chains and underconfident for “thetic” chains. Not shown in the plot, but slug-sail estimator had overall much higher variance than Geyer’s and AR approaches.

Although the differences between methods are clear, the effect of using the methods with bigger errors is likely to be small in most actual Bayesian modeling use cases. Except for the shortest chains with the highest autocorrelations, the errors in MCSE are at highest 20%, which should provide sufficient information whether more draws are needed. With more draws the accuracy of all methods improve.

Mean of x^2

In the above the chains were simulated from AR(1) process, so we would expect that coda: AR method would have some advantage. Also the chains have normal(0,1) marginal which makes the empirical means also to behave nicely. As an alternative we test the methods also with the same AR processes, but values are squared so that the chains are non-linear transformation of the AR process and the marginal is skewed. Squared variable is, for example, a natural part of variance computation.

x1 = arima.sim(n = N, list(ar = c(phi)), sd = sqrt((1-phi^2)))^2;
x2 = arima.sim(n = N, list(ar = c(phi)), sd = sqrt((1-phi^2)))^2;
x3 = arima.sim(n = N, list(ar = c(phi)), sd = sqrt((1-phi^2)))^2;
x4 = arima.sim(n = N, list(ar = c(phi)), sd = sqrt((1-phi^2)))^2;

The chosen \(\phi\) values correspond to now ESS/\(S\) values \((0.8, 1.0, 1.0, 0.8, 0.6, 0.3, 0.1)\). The squaring breaks the antithetic behavior, but for simplicity we show the results for all different values of \(\phi\).

Again posterior: Geyer is on average clearly closer to 1 than the other methods. All methods have bigger errors for the short chains (S=4 x 100), especially when ESS/\(S\) is small, which is natural as the draws are from a skewed distribution and the variability in means and correlations increases. With increasing number of draws the error decreases for all methods.

Probability p(x<0)

Next we examine the behavior for estimating probabilities. The sequence is now binary with values 0 and 1, computed with an indicator function I(x<0).

x1 = as.double(arima.sim(n = N, list(ar = c(phi)), sd = sqrt((1-phi^2)))<0);
x2 = as.double(arima.sim(n = N, list(ar = c(phi)), sd = sqrt((1-phi^2)))<0);
x3 = as.double(arima.sim(n = N, list(ar = c(phi)), sd = sqrt((1-phi^2)))<0);
x4 = as.double(arima.sim(n = N, list(ar = c(phi)), sd = sqrt((1-phi^2)))<0);

The chosen \(\phi\) values correspond to now ESS/\(S\) values \((1.4, 1.1, 0.9, 0.6, 0.4, 0.2, 0.1)\). We see super-efficiency for antithetic chains.

Again posterior: Geyer is on average clearly closer to 1 than the other methods. All methods have bigger errors for the short chains (S=4 x 100), especially when ESS/\(S\) is small, which is natural as binary observation contain less information than continuous observations. With increasing number of draws the error decreases for all methods.

Median of x

Quantiles are not expectations, but can be easily estimated from MCMC draws. Computing MCSE requires additional steps. stableGR and coda packages don’t have a function for computing MCSE for quantiles (or at least it is not mentioned in the documentation). mcmcse package function mcse.q uses batch means approach. posterior package function mcse_quantile uses the inversion approach presented by Vehtari et al. (2021).

The plots for posterior: Geyer and mcmcse: batch means look similar as in the probability estimation which is expected due to the close connection between computing cumulative probability and quantile value. posterior: Geyer line is on average closer to 1.

Comparison of ESS estimators for bimodal distribution

Above, AR process was used to simulate MCMC sampling from a unimodal distribution. We advocate running many chains not just for the parallelization of the computation, but also to be able to detect possible multimodality. Here we sample from a bimodal distribution using Stan. We vary the number of chains and examine the behavior of the ESS estimators in the same three packages.

posterior package is the only one of these packages taking into account the the possibility that the chains are not mixing by using also multi-chain in ESS and MCSE computations. stableGR and coda accept multiple chains as input, but don’t show in ESS estimates indication of between chain mixing problems. mcmcse accepts only one chain as input, and multiple chains were simply concatenated to provide a “single” chain as input.

The model is a simple Student’s \(t_4\) distribution with unknown location and scale.

data {int<lower=0> N; vector[N] y }
parameters {real mu;}
model {mu ~ student_t(4, 0, 100); y ~ student_t(4, mu, 1);}

The data is generated from a bimodal distribution.

N=20; y=c(rnorm(N/2, mean=-5, sd=1),rnorm(N/2, mean=5, sd=1));

With this data, the posterior is bimodal with well separated modes. The modes don’t necessarily have equal posterior mass. Stan’s dynamic HMC is not able to jump from one mode to another. The random initialization determines to which mode each chain ends up, and it is not possible to infer the actual relative posterior masses from the chains. The number of chains in each mode is random, and the probability of a chain with random initialization ending to a mode depends on the volume of the attraction area of that mode. In such case we would like to get a warning that the chains are not mixing.

The following plot shows an example of trace plot when one chain is stuck in one mode and three in another mode.

If we would repeat the sampling with random initializations and four chains, we would observe 0–4 chains stuck in one mode and the rest in the other mode. So there is still a possibility that all the chains are stuck in one mode and we think that the posterior is unimodal. With just one chain, we have probability of 0 recognizing that there are well separated modes. With more chains, it is possible that our random initialization is within the attraction area of just one mode, and then having more chains doesn’t help. In general, with increasing number of chains, we have higher probability of seeing more than one mode.

How should ESS estimates look like when we have chains that are not mixing? If the modes are well separated so that between-chain variance is much bigger than within-chain variances, each chain is mostly indicating the loaction of the mode and not worth much more than one observation. The posterior package combines Geyer truncated autocorrelation estimates with multi-chain convergence diagnostic \(\widehat{R}\). If the chains are not mixing, then the draws within each chain are considered to be more correlated within the chain and the multi-chain ESS estimate gets smaller. If the modes are well separated, multi-chain ESS is close to the number of chains. Arguably even this is an overestimate as increasing the number of non-mixing chains does not provide reliable estimate of the relative masses of the masses.

The following plot shows ESS per chain estimates using the four packages and with increasing number of chains. Simulations have been repeated 100 times.

With one chain, all packages consider only the within-chain correlation and report similar ESS per chain values (~350). When different chains end up to stuck in different well-separated modes, we would prefer to get an estimate that is indicating poor efficiency. With increasing number of chains, coda keeps reporting similar ESS per chain (with less variation) and provides no indication of non-mixing chains. When at least on chain is stuck in a different mode, stableGR reports ESS per chain of approximately 60, which is much less than 350, but we could see similar low values due to high autocorrelation and the total ESS is high enough not to indicate that chains are not mixing well. mcmcse does not have support for multiple chains, but combining the chains to one chain, changes the autocorrelation estimate so that when the number of chains increases, eventually ESS per chain decreases close to 1, indicating inefficient sampling. posterior reports ESS per chain close to 1 always when at least one chain is in the other mode, which is a clear indication that something is wrong in the mixing of the chains.

References

  • Geyer, C. J. (1992). Practical Markov chain Monte Carlo. Statistical Science, 7:473–483.
  • Gong, L., and Flegal, J. M. (2015). A practical sequential stopping rule for high-dimensional Markov chain Monte Carlo. Journal of Computational and Graphical Statistics, 25:684—700.
  • Heidelberger, P., and Welch, P. D. (1981). A spectral method for confidence interval generation and run length control in simulations. Communications of the ACM, 24:233—245.
  • Vats, D., and Knudson, C. (2018). Revisiting the Gelman-Rubin diagnostic. arXiv preprint, arXiv:1812.09384.
  • Vats, D., and Knudson, C. (2021). Revisiting the Gelman-Rubin diagnostic. Statistical Science, 36(4): 518—529.
  • Vehtari, A., Gelman, A., Simpson, D., Carpenter, B., Bürkner, P.-C. (2021): Rank-normalization, folding, and localization: An improved \(\widehat{R}\) for assessing convergence of MCMC. Bayesian analysis, 16(2):667—718. doi:10.1214/20-BA1221.
LS0tCnRpdGxlOiAiQ29tcGFyaXNvbiBvZiBNQ01DIGVmZmVjdGl2ZSBzYW1wbGUgc2l6ZSBlc3RpbWF0b3JzIgphdXRob3I6ICJBa2kgVmVodGFyaSIKZGF0ZTogIkZpcnN0IHZlcnNpb24gMjAyMS0xMS0wMy4gTGFzdCBtb2RpZmllZCBgciBmb3JtYXQoU3lzLkRhdGUoKSlgLiIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICB0aGVtZTogcmVhZGFibGUKICAgIHRvYzogdHJ1ZQogICAgdG9jX2RlcHRoOiAzCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKLS0tCgojIyBQb3N0IHNjcmlwdCBBcHBlbmRpeAoKVGhpcyBub3RlYm9vayBpcyBhIHBvc3Qgc2NyaXB0IGFwcGVuZGl4IHRvIHRoZSBwYXBlciA8YnI+CgotIEFraSBWZWh0YXJpLCBBbmRyZXcgR2VsbWFuLCBEYW5pZWwgU2ltcHNvbiwgQm9iIENhcnBlbnRlciwKUGF1bC1DaHJpc3RpYW4gQsO8cmtuZXIgKDIwMjEpOiBSYW5rLW5vcm1hbGl6YXRpb24sIGZvbGRpbmcsIGFuZApsb2NhbGl6YXRpb246IEFuIGltcHJvdmVkICRcd2lkZWhhdHtSfSQgZm9yIGFzc2Vzc2luZyBjb252ZXJnZW5jZSBvZiBNQ01DLiAqQmF5ZXNpYW4gYW5hbHlzaXMqLCAxNigyKTo2NjctNzE4LiBbZG9pOjEwLjEyMTQvMjAtQkExMjIxXShodHRwczovL2RvaS5vcmcvMTAuMTIxNC8yMC1CQTEyMjEpLgoKVGhlIGNvZGUgdXNlZCB0byBtYWtlIHRoaXMgbm90ZWJvb2sgaXMgYXZhaWxhYmxlIGF0IFtodHRwczovL2dpdGh1Yi5jb20vYXZlaHRhcmkvcmhhdF9lc3NdKGh0dHBzOi8vZ2l0aHViLmNvbS9hdmVodGFyaS9yaGF0X2VzcykuCgojIyBJbnRyb2R1Y3Rpb24KClRoZSBNQ01DIGVmZmVjdGl2ZSBzYW1wbGUgc2l6ZSAoRVNTKSBhbmQgTW9udGUgQ2FybG8gc3RhbmRhcmQgZXJyb3IKKE1DU0UpIGVzdGltYXRlZCBmb3Igb25lIGNoYWluIGluY2x1ZGVzIGVzdGltYXRpb24gb2YgdGhlCmNvcnJlbGF0aW9uIGJldHdlZW4gdGhlIGl0ZXJhdGlvbnMsIGZvciBleGFtcGxlLCB1c2luZwphdXRvY29ycmVsYXRpb24gdGltZSAob3Igc3BlY3RyYWwgZGVuc2l0eSBhdCBmcmVxdWVuY3kgemVybykuIEFzIGEKZmluaXRlIG51bWJlciBvZiBNQ01DIGRyYXdzIGFyZSBhdmFpbGFibGUgdGhlIGF1dG9jb3JyZWxhdGlvbgplc3RpbWF0ZXMgZm9yIGJpZ2dlciBsYWdzIGFyZSBub2lzaWVyLiAgSW4gdGhlIHBhcGVyLCB3ZSB3cm90ZQphYm91dCBhdXRvY29ycmVsYXRpb24gZXN0aW1hdG9ycwoKPiBJbiBvdXIgZXhwZXJpbWVudHMsIEdleWVyJ3MgWygxOTkyKV0gdHJ1bmNhdGlvbiBoYWQgc3VwZXJpb3IKPiBzdGFiaWxpdHkgY29tcGFyZWQgdG8gZmxhdC10b3AgKERvc3MgZXQgYWwuLCAyMDE0KSBhbmQgc2x1Zy1zYWlsCj4gKFZhdHMgYW5kIEtudWRzb24sIDIwMTgpIGxhZyB3aW5kb3cgYXBwcm9hY2hlcy4KCihWYXRzIGFuZCBLbnVkc29uICgyMDE4KSBoYXMgc2luY2UgYmVlbiBwdWJsaXNoZWQgYXMgVmF0cyBhbmQKS251ZHNvbiAoMjAyMSkpLiAgQXMgdGhlIG1haW4gcG9pbnQgb2YgdGhlIHBhcGVyIHdhcyBub3QgY29tcGFyaXNvbgpvZiBhdXRvY29ycmVsYXRpb24gZXN0aW1hdG9ycywgdGhlIGV4cGVyaW1lbnRhbCByZXN1bHRzIHdlcmUgbm90CmluY2x1ZGVkIGluIHRoZSBwYXBlci4KClRoaXMgbm90ZWJvb2sgaW5jbHVkZXMgYWRkaXRpb25hbCBleHBlcmltZW50cyBjb21wYXJpbmcgdGhlIEdleWVyJ3MKKDE5OTIpIHRydW5jYXRpb24gYXBwcm9hY2ggdG8gdGhyZWUgb3RoZXIgbWV0aG9kcyBmb3IgZXN0aW1hdGluZwplZmZlY3RpdmUgc2FtcGxlIHNpemUgKEVTUykgYW5kIGNvcnJlc3BvbmRpbmcgTW9udGUgQ2FybG8gc3RhbmRhcmQKZXJyb3IgKE1DU0UpLiBXZSBhbHNvIGNvbXBhcmUgdGhlIG5ldyBxdWFudGlsZSBNQ1NFIG1ldGhvZCB3ZQpwcm9wb3NlZCBpbiB0aGUgcGFwZXIgKFZlaHRhcmkgZXQgYWwuLCAyMDIxKSB0byBiYXRjaCBtZWFucwpxdWFudGlsZSBNQ1NFIG1ldGhvZCBieSAoR29uZyBhbmQgRmxlZ2FsLCAyMDE1KS4gRmluYWxseSwgd2UKY29tcGFyZSB0aGUgYmVoYXZpb3Igb2YgRVNTIGltcGxlbWVudGF0aW9ucyBpbiBmb3VyIFIgcGFja2FnZXMgaW4KY2FzZSBvZiBtdWx0aXBsZSBjaGFpbnMgYW5kIHdlbGwgc2VwYXJhdGVkIG11bHRpbW9kYWwgZGlzdHJpYnV0aW9uLgoKIyMjIFRoZSBtZXRob2RzIGFuZCBSIHBhY2thZ2VzIGNvbXBhcmVkCgotIFtgc3RhYmxlR1JgXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy9wYWNrYWdlPXN0YWJsZUdSKSAoYG4uZWZmYCkgY29tcHV0ZXMgYSB3ZWlnaHRlZCBzdW0gb2YgYXV0b2NvcnJlbGF0aW9ucyB1c2luZyBzbHVnLXNhaWwgbGFnIHdpbmRvdyAoVmF0cyBhbmQgS251ZHNvbiwgMjAxOCkgCi0gWydtY21jc2UnXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy9wYWNrYWdlPW1jbWNzZSkgKGBlc3NgIGFuZCBgbWNzZS5xYCkgdXNlcyBieSBkZWZhdWx0IGEgYmF0Y2ggbWVhbnMgYXBwcm9hY2ggKEdvbmcgYW5kIEZsZWdhbCwgMjAxNSkgCi0gW2Bjb2RhYF0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvcGFja2FnZT1jb2RhKSAoYGVmZmVjdGl2ZVNpemVgKSBjb21wdXRlcyB0aGUgc3BlY3RyYWwgZGVuc2l0eSBhdCBmcmVxdWVuY3kgemVybyBieSBmaXR0aW5nIGFuIEFSIG1vZGVsIHRvIHRoZSBjaGFpbiAoSGVpZGVsYmVyZ2VyIGFuZCBXZWxjaCwgMTk4MSkKLSBbYHBvc3RlcmlvcmBdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3BhY2thZ2U9cG9zdGVyaW9yKTogYGVzc19iYXNpY2AgY29tcHV0ZXMgc3VtIG9mIGF1dG9jb3JyZWxhdGlvbnMgdXNpbmcgR2V5ZXIncyB0cnVuY2F0aW9uIHJ1bGUgKEdleWVyLCAxOTkyKSwgYW5kIGBtY3NlX3F1YW50aWxlYCB1c2VzIGluIGFkZGl0aW9uIGFuIGludmVyc2UgYXBwcm9hY2ggZm9yIHF1YW50aWxlIE1DU0UuIFRoZSBgcG9zdGVyaW9yYCBwYWNrYWdlIGFsc28gdGFrZXMgaW50byBhY2NvdW50IHRoZSB0aGUgcG9zc2liaWxpdHkgdGhhdCB0aGUgY2hhaW5zIGFyZSBub3QgbWl4aW5nIGJ5IHVzaW5nIGFsc28gbXVsdGktY2hhaW4gJFx3aWRlaGF0e1J9JCBpbiBFU1MgYW5kIE1DU0UgY29tcHV0YXRpb25zLgoKIyMjIHRsO2RyCgpJbiBjYXNlIG9mIHdlbGwgbWl4aW5nIGNoYWlucywgYGVzc19iYXNpY2AgYW5kIGBtY3NlX3F1YW50aWxlYCBpbgp0aGUgYHBvc3RlcmlvcmAgcGFja2FnZSBhcmUgdGhlIG1vc3QgYWNjdXJhdGUgbWV0aG9kcy4gSW4gY2FzZSBvZgp3ZWxsIHNlcGFyYXRlZCBtb2RlcywgdGhlIGBwb3N0ZXJpb3JgIHBhY2thZ2UgYWxzbyBwcm92aWRlcwphcHByb3ByaWF0ZWx5IHNtYWxsIEVTUyBpbmRpY2F0aW5nIHRoYXQgdGhlIGNoYWlucyBhcmUgbm90IG1peGluZy4KCgpgYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChtZXNzYWdlPUZBTFNFLCBlcnJvcj1GQUxTRSwgd2FybmluZz1GQUxTRSwgY29tbWVudD1OQSwgY2FjaGU9RkFMU0UpCmBgYGAKYGBgYHtyIGxvYWRfcGFja2FnZXMsIGVjaG89RkFMU0V9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGJheWVzcGxvdCkKdGhlbWVfc2V0KGJheWVzcGxvdDo6dGhlbWVfZGVmYXVsdChiYXNlX2ZhbWlseSA9ICJzYW5zIikpCmxpYnJhcnkoa2hyb21hKQpsaWJyYXJ5KGxhdGV4MmV4cCkKYGBgYAoKCiMjIENvbXBhcmlzb24gb2YgRVNTIGVzdGltYXRvcnMgd2l0aCBBUigxKSBzaW11bGF0aW9uCgojIyMgTWVhbiBvZiB4CgpXZSBzaW11bGF0ZSAkTT00JCBjaGFpbnMgZnJvbSBhIGtub3duIEFSKDEpIHByb2Nlc3Mgd2l0aCB2YXJ5aW5nCnBhcmFtZXRlciAkXHBoaSQsIGFuZCBub3JtYWwoMCwxKSBtYXJnaW5hbCBkaXN0cmlidXRpb24uCmBgYAp4MSA9IGFyaW1hLnNpbShuID0gTiwgbGlzdChhciA9IGMocGhpKSksIHNkID0gc3FydCgoMS1waGleMikpKTsKeDIgPSBhcmltYS5zaW0obiA9IE4sIGxpc3QoYXIgPSBjKHBoaSkpLCBzZCA9IHNxcnQoKDEtcGhpXjIpKSk7CngzID0gYXJpbWEuc2ltKG4gPSBOLCBsaXN0KGFyID0gYyhwaGkpKSwgc2QgPSBzcXJ0KCgxLXBoaV4yKSkpOwp4NCA9IGFyaW1hLnNpbShuID0gTiwgbGlzdChhciA9IGMocGhpKSksIHNkID0gc3FydCgoMS1waGleMikpKTsKYGBgCgpXZSB2YXJ5IHRoZSBsZW5ndGggb2YgdGhlIGNoYWlucywgJE4kLCBhbmQgdGhlIEFSIHByb2Nlc3MgcGFyYW1ldGVyICRccGhpJApgYGAKTnM9YygxMDAsIDEwMDAsIDEwMDAwKQpwaGlzPWMoLTAuMywgLTAuMSwgMC4xLCAwLjMsIDAuNSwgMC43LCAwLjkpCmBgYAoKJE49MTAwMCQgaXMgdGhlIGRlZmF1bHQgaW4gU3RhbiwgYW5kICROPTEwMCQgYW5kICROPTEwMDAwJCBhcmUgdXNlZAp0byBpbGx1c3RyYXRlIHRoZSBiZWhhdmlvciB3aXRoIGxlc3Mgb3IgbW9yZSBNQ01DIGl0ZXJhdGlvbnMuIFRoZQp0b3RhbCBudW1iZXIgb2YgZHJhd3MgaXMgJFM9TSpOJC4KClRoZSB2YWx1ZXMgb2YgJFxwaGkkIGhhdmUgYmVlbiBjaG9zZW4gdG8gcmVmbGVjdCB0eXBpY2FsIEVTUy8kUyQKdmFsdWVzIHNlZW4gZnJvbSBTdGFuLiBUaGUgY2hvc2VuICRccGhpJCB2YWx1ZXMgY29ycmVzcG9uZCB0byBFU1MvJFMkCnZhbHVlcyAkKDEuOSwgMS4yLCAwLjgsIDAuNSwgMC4zLCAwLjIsIDAuMSkkLiBUaGUgdHdvIGZpcnN0ICRccGhpJAp2YWx1ZXMgYmVpbmcgbmVnYXRpdmUgcHJvZHVjZSBhbnRpdGhldGljIGNoYWlucyB3aXRoIEVTUz4kUyQsIHdoaWNoCmlzIG5vdCByYXJlIHdoZW4gdXNpbmcgU3RhbidzIGR5bmFtaWMgSGFtaWx0b25pYW4gTW9udGUgQ2FybG8uCgpTaW11bGF0aW9uIGlzIHJlcGVhdGVkICQxMDBcLDAwMCQgdGltZXMgZm9yIGVhY2ggY29tYmluYXRpb24gb2YgJE4kCmFuZCAkXHBoaSQuIEZvciBlYWNoIHNpbXVsYXRpb24gd2UgY29tcHV0ZSB0aGUgZW1waXJpY2FsIG1lYW4uIFRoZQp2YXJpYW5jZSBvZiB0aGUgZW1waXJpY2FsIG1lYW5zIGFuZCBrbm93biB0cnVlIHZhcmlhbmNlIGFyZQp1c2VkIHRvIGNvbXB1dGUgdGhlIGFzeW1wdG90aWMgZWZmaWNpZW5jeSwgdGhhdCBpcywgdHJ1ZSBFU1MvJFMkIHdoZW4KJFNccmlnaHRhcnJvdyBcaW5mdHkkLiBGb3IgZWFjaCBzaW11bGF0aW9uIHdlIGNvbXB1dGUgdGhyZWUgRVNTCmVzdGltYXRlcyBhbmQgdXNlIHRob3NlIHRvIGNvbXB1dGUgTW9udGUgQ2FybG8gc3RhbmRhcmQgZXJyb3IKKE1DU0UpIHdoaWNoIGlzIGNvbXBhcmVkIHRvIHRoZSBhY3R1YWwgZXJyb3IsIHRoYXQgaXMsIHRoZQpkaWZmZXJlbmNlIGJldHdlZW4gdGhlIGVtcGlyaWNhbCBtZWFuICRcaGF0e1xtdX0kIGFuZCBrbm93biB0cnVlCm1lYW4gJFxtdT0wJC4KClRoZSBmb2xsb3dpbmcgZmlndXJlIHN1bW1hcml6ZXMgdGhlIHJlc3VsdHMuICBJbiB0aGUgY2FzZSBvZgplc3RpbWF0aW5nIHNvbWUgcG9zdGVyaW9yIGV4cGVjdGF0aW9uICRcbWF0aHJte0V9W2coeCldJCwgdGhlIHVzZWQKRVNTIGVzdGltYXRvcnMgYXNzdW1lIGZpbml0ZSB2YXJpYW5jZSBhbmQgTUNTRSBpcyBzaW1wbHkKJFxtYXRocm17c2R9W2coeCldL1xzcXJ0e1xtYXRocm17RVNTfX0kLiBUaHVzIGFjY3VyYWN5IG9mIEVTUyBpcwpkaXJlY3RseSByZWxhdGVkIHRvIHRoZSBhY2N1cmFjeSBvZiBNQ1NFLiBXZSByZXBvcnQgcm9vdCBtZWFuCnNxdWFyZSBvZiBzdGFuZGFyZGl6ZWQgZXJyb3JzICR8XG11IC0KXGhhdHtcbXV9fC9cd2lkZWhhdHtcbWF0aHJte01DU0V9fSQsIHdoaWNoIGlzIGluIGNhc2Ugb2Ygd2VsbCBjYWxpYnJhdGVkIEVTUwphbmQgTUNTRSBzaG91bGQgYmUgY2xvc2UgdG8gMS4gVmFsdWVzIGxlc3MgdGhhbiAxIGluZGljYXRlCnVuZGVyZXN0aW1hdGVkIEVTUyBhbmQgb3ZlcmVzdGltYXRlZCBNQ1NFLCBhbmQgdmFsdWVzIGxhcmdlciB0aGFuIDEKaW5kaWNhdGUgb3ZlcmVzdGltYXRlZCBFU1MgYW5kIHVuZGVyZXN0aW1hdGVkIE1DU0UuCgoKYGBgYHtyIHBsb3QxLCBlY2hvPUZBTFNFLCBmaWcuZGltPWMoOSwzKX0KbG9hZCgnZXNzX2FyX2RmLlJkYXRhJykKTnM9YygxMDAsIDEwMDAsIDEwMDAwKQpwaGlzPWMoLTAuMywgLTAuMSwgMC4xLCAwLjMsIDAuNSwgMC43LCAwLjkpCmdncGxvdChkZiwgYWVzKHg9cGhpLCB5PXosIAogICAgICAgICAgICAgICBncm91cD1tZXRob2QsCiAgICAgICAgICAgICAgIGNvbG9yPW1ldGhvZCkpICsKICBnZW9tX2xpbmUoYWxwaGE9MC44LCBzaXplPTEpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAxLCBsaW5ldHlwZT0nZGFzaGVkJykgKwogIGNvb3JkX2NhcnRlc2lhbih5bGltPWMoMC45MSwxLjMpLCBjbGlwPSdvZmYnKSsKICBsYWJzKHg9J1RydWUgRVNTL1MnLCB5PVRlWCgnUk1TIG9mICR8XFxtdSAtXFxoYXR7XFxtdX18IC8gXFx3aWRlaGF0e01DU0V9JCcpKSsKICBndWlkZXMobGluZXR5cGUgPSBndWlkZV9sZWdlbmQodGl0bGUgPSAiTiIpLAogICAgICAgICBjb2xvciA9IGd1aWRlX2xlZ2VuZCh0aXRsZSA9ICJQYWNrYWdlOiBNZXRob2QiKSkrCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcz1waGlzLCBsYWJlbHMgPSByb3VuZCh0ZXNzLzQwMDAwLDEpKSsKICBzY2FsZV9jb2xvcl9icmlnaHQoYnJlYWtzPWMoInN0YWJsZUdSOjpuLmVmZiIsIm1jbWNzZTo6ZXNzIiwiY29kYTo6ZWZmZWN0aXZlU2l6ZSIsInBvc3Rlcmlvcjo6ZXNzX2Jhc2ljIiksIGxhYmVscz1jKCdzdGFibGVHUjogU2x1Zy1zYWlsJywnbWNtY3NlOiBCYXRjaCBtZWFucycsJ2NvZGE6IEFSJywncG9zdGVyaW9yOiBHZXllcicpKSsKICBmYWNldF9ncmlkKGNvbHM9dmFycyhTPVMpKQpgYGBgCgoKYHBvc3RlcmlvcjogR2V5ZXJgIGlzIG9uIGF2ZXJhZ2UgY2xlYXJseSBjbG9zZXIgdG8gMSB0aGFuIHRoZSBvdGhlcgptZXRob2RzLiBgY29kYTogQVJgIG1ldGhvZCBpcyBvdmVyY29uZmlkZW50IHdpdGggc2hvcnQgY2hhaW5zLCBidXQKaGFzIGdvb2QgYmVoYXZpb3Igd2l0aCBsb25nZXIgY2hhaW5zLiBgc3RhYmxlR1I6IFNsdWctc2FpbGAgaGFzCnF1aXRlIHVucHJlZGljdGFibGUgYmVoYXZpb3IgYmVpbmcgZWl0aGVyIG92ZXItIG9yIHVuZGVyY29uZmlkZW50LApleGNlcHQgaW4gY2FzZSBvZiB2ZXJ5IGxvbmcgY2hhaW5zIHdoZW4gdGhlIG1ldGhvZCBpcyBjb25zaXN0ZW50bHkKb3ZlcmNvbmZpZGVudCBmb3IgYW50aXRoZXRpYyBjaGFpbnMgYW5kIHVuZGVyY29uZmlkZW50IGZvcgoidGhldGljIiBjaGFpbnMuIE5vdCBzaG93biBpbiB0aGUgcGxvdCwgYnV0IHNsdWctc2FpbCBlc3RpbWF0b3IKaGFkIG92ZXJhbGwgbXVjaCBoaWdoZXIgdmFyaWFuY2UgdGhhbiBHZXllcidzIGFuZCBBUiBhcHByb2FjaGVzLgoKQWx0aG91Z2ggdGhlIGRpZmZlcmVuY2VzIGJldHdlZW4gbWV0aG9kcyBhcmUgY2xlYXIsIHRoZSBlZmZlY3Qgb2YKdXNpbmcgdGhlIG1ldGhvZHMgd2l0aCBiaWdnZXIgZXJyb3JzIGlzIGxpa2VseSB0byBiZSBzbWFsbCBpbiBtb3N0CmFjdHVhbCBCYXllc2lhbiBtb2RlbGluZyB1c2UgY2FzZXMuIEV4Y2VwdCBmb3IgdGhlIHNob3J0ZXN0IGNoYWlucwp3aXRoIHRoZSBoaWdoZXN0IGF1dG9jb3JyZWxhdGlvbnMsIHRoZSBlcnJvcnMgaW4gTUNTRSBhcmUgYXQKaGlnaGVzdCAyMFwlLCB3aGljaCBzaG91bGQgcHJvdmlkZSBzdWZmaWNpZW50IGluZm9ybWF0aW9uIHdoZXRoZXIKbW9yZSBkcmF3cyBhcmUgbmVlZGVkLiBXaXRoIG1vcmUgZHJhd3MgdGhlIGFjY3VyYWN5IG9mIGFsbCBtZXRob2RzCmltcHJvdmUuCgojIyMgTWVhbiBvZiB4XjIKCkluIHRoZSBhYm92ZSB0aGUgY2hhaW5zIHdlcmUgc2ltdWxhdGVkIGZyb20gQVIoMSkgcHJvY2Vzcywgc28gd2Ugd291bGQKZXhwZWN0IHRoYXQgYGNvZGE6IEFSYCBtZXRob2Qgd291bGQgaGF2ZSBzb21lIGFkdmFudGFnZS4gQWxzbyB0aGUKY2hhaW5zIGhhdmUgbm9ybWFsKDAsMSkgbWFyZ2luYWwgd2hpY2ggbWFrZXMgdGhlIGVtcGlyaWNhbCBtZWFucwphbHNvIHRvIGJlaGF2ZSBuaWNlbHkuIEFzIGFuIGFsdGVybmF0aXZlIHdlIHRlc3QgdGhlIG1ldGhvZHMgYWxzbwp3aXRoIHRoZSBzYW1lIEFSIHByb2Nlc3NlcywgYnV0IHZhbHVlcyBhcmUgc3F1YXJlZCBzbyB0aGF0IHRoZQpjaGFpbnMgYXJlIG5vbi1saW5lYXIgdHJhbnNmb3JtYXRpb24gb2YgdGhlIEFSIHByb2Nlc3MgYW5kIHRoZQptYXJnaW5hbCBpcyBza2V3ZWQuIFNxdWFyZWQgdmFyaWFibGUgaXMsIGZvciBleGFtcGxlLCBhIG5hdHVyYWwgcGFydApvZiB2YXJpYW5jZSBjb21wdXRhdGlvbi4KYGBgCngxID0gYXJpbWEuc2ltKG4gPSBOLCBsaXN0KGFyID0gYyhwaGkpKSwgc2QgPSBzcXJ0KCgxLXBoaV4yKSkpXjI7CngyID0gYXJpbWEuc2ltKG4gPSBOLCBsaXN0KGFyID0gYyhwaGkpKSwgc2QgPSBzcXJ0KCgxLXBoaV4yKSkpXjI7CngzID0gYXJpbWEuc2ltKG4gPSBOLCBsaXN0KGFyID0gYyhwaGkpKSwgc2QgPSBzcXJ0KCgxLXBoaV4yKSkpXjI7Cng0ID0gYXJpbWEuc2ltKG4gPSBOLCBsaXN0KGFyID0gYyhwaGkpKSwgc2QgPSBzcXJ0KCgxLXBoaV4yKSkpXjI7CmBgYAoKVGhlIGNob3NlbiAkXHBoaSQgdmFsdWVzIGNvcnJlc3BvbmQgdG8gbm93IEVTUy8kUyQgdmFsdWVzICQoMC44LCAxLjAsCjEuMCwgMC44LCAwLjYsIDAuMywgMC4xKSQuIFRoZSBzcXVhcmluZyBicmVha3MgdGhlIGFudGl0aGV0aWMKYmVoYXZpb3IsIGJ1dCBmb3Igc2ltcGxpY2l0eSB3ZSBzaG93IHRoZSByZXN1bHRzIGZvciBhbGwgZGlmZmVyZW50CnZhbHVlcyBvZiAkXHBoaSQuCgoKYGBgYHtyIHBsb3QyLCBlY2hvPUZBTFNFLCBmaWcuZGltPWMoOSwzKX0KbG9hZCgnZXNzX2FyMl9kZi5SZGF0YScpCmdncGxvdChkZiwgYWVzKHg9cGhpLCB5PXosIAogICAgICAgICAgICAgICBncm91cD1tZXRob2QsCiAgICAgICAgICAgICAgIGNvbG9yPW1ldGhvZCkpICsKICBnZW9tX2xpbmUoYWxwaGE9MC44LCBzaXplPTEpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAxLCBsaW5ldHlwZT0nZGFzaGVkJykgKwogIGNvb3JkX2NhcnRlc2lhbih5bGltPWMoMC45MSwxLjQpLCBjbGlwPSdvZmYnKSsKICBsYWJzKHg9J1RydWUgRVNTL1MnLCB5PVRlWCgnUk1TIG9mICR8XFxtdSAtXFxoYXR7XFxtdX18IC8gXFx3aWRlaGF0e01DU0V9JCcpKSsKICBndWlkZXMobGluZXR5cGUgPSBndWlkZV9sZWdlbmQodGl0bGUgPSAiTiIpLAogICAgICAgICBjb2xvciA9IGd1aWRlX2xlZ2VuZCh0aXRsZSA9ICJNZXRob2QiKSkrCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcz1waGlzLCBsYWJlbHMgPSByb3VuZCh0ZXNzLzQwMDAwLDEpKSsKICBzY2FsZV9jb2xvcl9icmlnaHQoYnJlYWtzPWMoInN0YWJsZUdSOjpuLmVmZiIsIm1jbWNzZTo6ZXNzIiwiY29kYTo6ZWZmZWN0aXZlU2l6ZSIsInBvc3Rlcmlvcjo6ZXNzX2Jhc2ljIiksIGxhYmVscz1jKCdzdGFibGVHUjogU2x1Zy1zYWlsJywnbWNtY3NlOiBCYXRjaCBtZWFucycsJ2NvZGE6IEFSJywncG9zdGVyaW9yOiBHZXllcicpKSsKICBmYWNldF9ncmlkKGNvbHM9dmFycyhTPVMpKQpgYGBgCgoKQWdhaW4gYHBvc3RlcmlvcjogR2V5ZXJgIGlzIG9uIGF2ZXJhZ2UgY2xlYXJseSBjbG9zZXIgdG8gMSB0aGFuIHRoZQpvdGhlciBtZXRob2RzLiBBbGwgbWV0aG9kcyBoYXZlIGJpZ2dlciBlcnJvcnMgZm9yIHRoZSBzaG9ydCBjaGFpbnMKKFM9NCB4IDEwMCksIGVzcGVjaWFsbHkgd2hlbiBFU1MvJFMkIGlzIHNtYWxsLCB3aGljaCBpcyBuYXR1cmFsIGFzCnRoZSBkcmF3cyBhcmUgZnJvbSBhIHNrZXdlZCBkaXN0cmlidXRpb24gYW5kIHRoZSB2YXJpYWJpbGl0eSBpbgptZWFucyBhbmQgY29ycmVsYXRpb25zIGluY3JlYXNlcy4gV2l0aCBpbmNyZWFzaW5nIG51bWJlciBvZiBkcmF3cwp0aGUgZXJyb3IgZGVjcmVhc2VzIGZvciBhbGwgbWV0aG9kcy4KCiMjIyBQcm9iYWJpbGl0eSBwKHg8MCkKCk5leHQgd2UgZXhhbWluZSB0aGUgYmVoYXZpb3IgZm9yIGVzdGltYXRpbmcgcHJvYmFiaWxpdGllcy4gVGhlCnNlcXVlbmNlIGlzIG5vdyBiaW5hcnkgd2l0aCB2YWx1ZXMgMCBhbmQgMSwgY29tcHV0ZWQgd2l0aCBhbgppbmRpY2F0b3IgZnVuY3Rpb24gSSh4PDApLgpgYGAKeDEgPSBhcy5kb3VibGUoYXJpbWEuc2ltKG4gPSBOLCBsaXN0KGFyID0gYyhwaGkpKSwgc2QgPSBzcXJ0KCgxLXBoaV4yKSkpPDApOwp4MiA9IGFzLmRvdWJsZShhcmltYS5zaW0obiA9IE4sIGxpc3QoYXIgPSBjKHBoaSkpLCBzZCA9IHNxcnQoKDEtcGhpXjIpKSk8MCk7CngzID0gYXMuZG91YmxlKGFyaW1hLnNpbShuID0gTiwgbGlzdChhciA9IGMocGhpKSksIHNkID0gc3FydCgoMS1waGleMikpKTwwKTsKeDQgPSBhcy5kb3VibGUoYXJpbWEuc2ltKG4gPSBOLCBsaXN0KGFyID0gYyhwaGkpKSwgc2QgPSBzcXJ0KCgxLXBoaV4yKSkpPDApOwpgYGAKClRoZSBjaG9zZW4gJFxwaGkkIHZhbHVlcyBjb3JyZXNwb25kIHRvIG5vdyBFU1MvJFMkIHZhbHVlcyAkKDEuNCwKMS4xLCAwLjksIDAuNiwgMC40LCAwLjIsIDAuMSkkLiBXZSBzZWUgc3VwZXItZWZmaWNpZW5jeSBmb3IKYW50aXRoZXRpYyBjaGFpbnMuCgoKYGBgYHtyIHBsb3QzLCBlY2hvPUZBTFNFLCBmaWcuZGltPWMoOSwzKX0KbG9hZCgnZXNzX2FycF9kZi5SZGF0YScpCmdncGxvdChkZiwgYWVzKHg9cGhpLCB5PXosIAogICAgICAgICAgICAgICBncm91cD1tZXRob2QsCiAgICAgICAgICAgICAgIGNvbG9yPW1ldGhvZCkpICsKICBnZW9tX2xpbmUoYWxwaGE9MC44LCBzaXplPTEpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAxLCBsaW5ldHlwZT0nZGFzaGVkJykgKwogIGNvb3JkX2NhcnRlc2lhbih5bGltPWMoMC45MSwxLjQpLCBjbGlwPSdvZmYnKSsKICBsYWJzKHg9J1RydWUgRVNTL1MnLCB5PVRlWCgnUk1TIG9mICR8XFxtdSAtXFxoYXR7XFxtdX18IC8gXFx3aWRlaGF0e01DU0V9JCcpKSsKICBndWlkZXMobGluZXR5cGUgPSBndWlkZV9sZWdlbmQodGl0bGUgPSAiTiIpLAogICAgICAgICBjb2xvciA9IGd1aWRlX2xlZ2VuZCh0aXRsZSA9ICJNZXRob2QiKSkrCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcz1waGlzLCBsYWJlbHMgPSByb3VuZCh0ZXNzLzQwMDAwLDEpKSsKICBzY2FsZV9jb2xvcl9icmlnaHQoYnJlYWtzPWMoInN0YWJsZUdSOjpuLmVmZiIsIm1jbWNzZTo6ZXNzIiwiY29kYTo6ZWZmZWN0aXZlU2l6ZSIsInBvc3Rlcmlvcjo6ZXNzX2Jhc2ljIiksIGxhYmVscz1jKCdzdGFibGVHUjogU2x1Zy1zYWlsJywnbWNtY3NlOiBCYXRjaCBtZWFucycsJ2NvZGE6IEFSJywncG9zdGVyaW9yOiBHZXllcicpKSsKICBmYWNldF9ncmlkKGNvbHM9dmFycyhTPVMpKQpgYGBgCgoKQWdhaW4gYHBvc3RlcmlvcjogR2V5ZXJgIGlzIG9uIGF2ZXJhZ2UgY2xlYXJseSBjbG9zZXIgdG8gMSB0aGFuIHRoZQpvdGhlciBtZXRob2RzLiBBbGwgbWV0aG9kcyBoYXZlIGJpZ2dlciBlcnJvcnMgZm9yIHRoZSBzaG9ydCBjaGFpbnMKKFM9NCB4IDEwMCksIGVzcGVjaWFsbHkgd2hlbiBFU1MvJFMkIGlzIHNtYWxsLCB3aGljaCBpcyBuYXR1cmFsIGFzCmJpbmFyeSBvYnNlcnZhdGlvbiBjb250YWluIGxlc3MgaW5mb3JtYXRpb24gdGhhbiBjb250aW51b3VzCm9ic2VydmF0aW9ucy4gIFdpdGggaW5jcmVhc2luZyBudW1iZXIgb2YgZHJhd3MgdGhlIGVycm9yIGRlY3JlYXNlcwpmb3IgYWxsIG1ldGhvZHMuCgojIyMgTWVkaWFuIG9mIHgKClF1YW50aWxlcyBhcmUgbm90IGV4cGVjdGF0aW9ucywgYnV0IGNhbiBiZSBlYXNpbHkgZXN0aW1hdGVkIGZyb20KTUNNQyBkcmF3cy4gQ29tcHV0aW5nIE1DU0UgcmVxdWlyZXMgYWRkaXRpb25hbCBzdGVwcy4gYHN0YWJsZUdSYAphbmQgYGNvZGFgIHBhY2thZ2VzIGRvbid0IGhhdmUgYSBmdW5jdGlvbiBmb3IgY29tcHV0aW5nIE1DU0UgZm9yCnF1YW50aWxlcyAob3IgYXQgbGVhc3QgaXQgaXMgbm90IG1lbnRpb25lZCBpbiB0aGUKZG9jdW1lbnRhdGlvbikuIGBtY21jc2VgIHBhY2thZ2UgZnVuY3Rpb24gYG1jc2UucWAgdXNlcyBiYXRjaCBtZWFucwphcHByb2FjaC4gYHBvc3RlcmlvcmAgcGFja2FnZSBmdW5jdGlvbiBgbWNzZV9xdWFudGlsZWAgdXNlcyB0aGUKaW52ZXJzaW9uIGFwcHJvYWNoIHByZXNlbnRlZCBieSBWZWh0YXJpIGV0IGFsLiAoMjAyMSkuCgoKYGBgYHtyIHBsb3Q0LCBlY2hvPUZBTFNFLCBmaWcuZGltPWMoOSwzKX0KbG9hZCgnZXNzX2FycV9kZi5SZGF0YScpCmNscjwtY29sb3VyKCJicmlnaHQiLG5hbWVzPUZBTFNFKSg3KQpnZ3Bsb3QoZGYsIGFlcyh4PXBoaSwgeT16LCAKICAgICAgICAgICAgICAgZ3JvdXA9bWV0aG9kLAogICAgICAgICAgICAgICBjb2xvcj1tZXRob2QpKSArCiAgZ2VvbV9saW5lKGFscGhhPTAuOCwgc2l6ZT0xKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMSwgbGluZXR5cGU9J2Rhc2hlZCcpICsKICBjb29yZF9jYXJ0ZXNpYW4oeWxpbT1jKDAuOTUsMS4yNSksIGNsaXA9J29mZicpKwogIGxhYnMoeD0nVHJ1ZSBFU1MvUycsIHk9VGVYKCdSTVMgb2YgJHxcXG11IC1cXGhhdHtcXG11fXwgLyBcXHdpZGVoYXR7TUNTRX0kJykpKwogIGd1aWRlcyhsaW5ldHlwZSA9IGd1aWRlX2xlZ2VuZCh0aXRsZSA9ICJOIiksCiAgICAgICAgIGNvbG9yID0gZ3VpZGVfbGVnZW5kKHRpdGxlID0gIk1ldGhvZCIpKSsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzPXBoaXMsIGxhYmVscyA9IHJvdW5kKHRlc3MvNDAwMDAsMSkpKwogIHNjYWxlX2NvbG9yX21hbnVhbChicmVha3M9YygibWNtY3NlOjptY3NlLnEiLCJwb3N0ZXJpb3I6Om1jc2VfcXVhbnRpbGUiKSwgbGFiZWxzPWMoJ21jbWNzZTogQmF0Y2ggbWVhbnMnLCdwb3N0ZXJpb3I6IEdleWVyICsgaW52LnByb2IuJyksIHZhbHVlcz1jKGNsclsyXSxjbHJbM10pKSsKICBmYWNldF9ncmlkKGNvbHM9dmFycyhTPVMpKQpgYGBgCgoKVGhlIHBsb3RzIGZvciBgcG9zdGVyaW9yOiBHZXllcmAgYW5kIGBtY21jc2U6IGJhdGNoIG1lYW5zYCBsb29rCnNpbWlsYXIgYXMgaW4gdGhlIHByb2JhYmlsaXR5IGVzdGltYXRpb24gd2hpY2ggaXMgZXhwZWN0ZWQgZHVlIHRvCnRoZSBjbG9zZSBjb25uZWN0aW9uIGJldHdlZW4gY29tcHV0aW5nIGN1bXVsYXRpdmUgcHJvYmFiaWxpdHkgYW5kCnF1YW50aWxlIHZhbHVlLiAgYHBvc3RlcmlvcjogR2V5ZXJgIGxpbmUgaXMgb24gYXZlcmFnZSBjbG9zZXIgdG8gMS4KCiMjIENvbXBhcmlzb24gb2YgRVNTIGVzdGltYXRvcnMgZm9yIGJpbW9kYWwgZGlzdHJpYnV0aW9uCgpBYm92ZSwgQVIgcHJvY2VzcyB3YXMgdXNlZCB0byBzaW11bGF0ZSBNQ01DIHNhbXBsaW5nIGZyb20gYQp1bmltb2RhbCBkaXN0cmlidXRpb24uIFdlIGFkdm9jYXRlIHJ1bm5pbmcgbWFueSBjaGFpbnMgbm90IGp1c3QgZm9yCnRoZSBwYXJhbGxlbGl6YXRpb24gb2YgdGhlIGNvbXB1dGF0aW9uLCBidXQgYWxzbyB0byBiZSBhYmxlIHRvCmRldGVjdCBwb3NzaWJsZSBtdWx0aW1vZGFsaXR5LiBIZXJlIHdlIHNhbXBsZSBmcm9tIGEgYmltb2RhbApkaXN0cmlidXRpb24gdXNpbmcgU3Rhbi4gV2UgdmFyeSB0aGUgbnVtYmVyIG9mIGNoYWlucyBhbmQgZXhhbWluZQp0aGUgYmVoYXZpb3Igb2YgdGhlIEVTUyBlc3RpbWF0b3JzIGluIHRoZSBzYW1lIHRocmVlIHBhY2thZ2VzLgoKYHBvc3RlcmlvcmAgcGFja2FnZSBpcyB0aGUgb25seSBvbmUgb2YgdGhlc2UgcGFja2FnZXMgdGFraW5nIGludG8KYWNjb3VudCB0aGUgdGhlIHBvc3NpYmlsaXR5IHRoYXQgdGhlIGNoYWlucyBhcmUgbm90IG1peGluZyBieSB1c2luZwphbHNvIG11bHRpLWNoYWluIFx3aWRlaGF0e1J9IGluIEVTUyBhbmQgTUNTRQpjb21wdXRhdGlvbnMuIGBzdGFibGVHUmAgYW5kIGBjb2RhYCBhY2NlcHQgbXVsdGlwbGUgY2hhaW5zIGFzCmlucHV0LCBidXQgZG9uJ3Qgc2hvdyBpbiBFU1MgZXN0aW1hdGVzIGluZGljYXRpb24gb2YgYmV0d2VlbiBjaGFpbgptaXhpbmcgcHJvYmxlbXMuIGBtY21jc2VgIGFjY2VwdHMgb25seSBvbmUgY2hhaW4gYXMgaW5wdXQsIGFuZAptdWx0aXBsZSBjaGFpbnMgd2VyZSBzaW1wbHkgY29uY2F0ZW5hdGVkIHRvIHByb3ZpZGUgYSAic2luZ2xlIgpjaGFpbiBhcyBpbnB1dC4KClRoZSBtb2RlbCBpcyBhIHNpbXBsZSBTdHVkZW50J3MgJHRfNCQgZGlzdHJpYnV0aW9uIHdpdGggdW5rbm93bgpsb2NhdGlvbiBhbmQgc2NhbGUuCmBgYApkYXRhIHtpbnQ8bG93ZXI9MD4gTjsgdmVjdG9yW05dIHkgfQpwYXJhbWV0ZXJzIHtyZWFsIG11O30KbW9kZWwge211IH4gc3R1ZGVudF90KDQsIDAsIDEwMCk7IHkgfiBzdHVkZW50X3QoNCwgbXUsIDEpO30KYGBgClRoZSBkYXRhIGlzIGdlbmVyYXRlZCBmcm9tIGEgYmltb2RhbCBkaXN0cmlidXRpb24uCmBgYApOPTIwOyB5PWMocm5vcm0oTi8yLCBtZWFuPS01LCBzZD0xKSxybm9ybShOLzIsIG1lYW49NSwgc2Q9MSkpOwpgYGAKCldpdGggdGhpcyBkYXRhLCB0aGUgcG9zdGVyaW9yIGlzIGJpbW9kYWwgd2l0aCB3ZWxsIHNlcGFyYXRlZAptb2Rlcy4gVGhlIG1vZGVzIGRvbid0IG5lY2Vzc2FyaWx5IGhhdmUgZXF1YWwgcG9zdGVyaW9yCm1hc3MuIFN0YW4ncyBkeW5hbWljIEhNQyBpcyBub3QgYWJsZSB0byBqdW1wIGZyb20gb25lIG1vZGUgdG8KYW5vdGhlci4gVGhlIHJhbmRvbSBpbml0aWFsaXphdGlvbiBkZXRlcm1pbmVzIHRvIHdoaWNoIG1vZGUgZWFjaApjaGFpbiBlbmRzIHVwLCBhbmQgaXQgaXMgbm90IHBvc3NpYmxlIHRvIGluZmVyIHRoZSBhY3R1YWwgcmVsYXRpdmUKcG9zdGVyaW9yIG1hc3NlcyBmcm9tIHRoZSBjaGFpbnMuIFRoZSBudW1iZXIgb2YgY2hhaW5zIGluIGVhY2ggbW9kZQppcyByYW5kb20sIGFuZCB0aGUgcHJvYmFiaWxpdHkgb2YgYSBjaGFpbiB3aXRoIHJhbmRvbQppbml0aWFsaXphdGlvbiBlbmRpbmcgdG8gYSBtb2RlIGRlcGVuZHMgb24gdGhlIHZvbHVtZSBvZiB0aGUKYXR0cmFjdGlvbiBhcmVhIG9mIHRoYXQgbW9kZS4gIEluIHN1Y2ggY2FzZSB3ZSB3b3VsZCBsaWtlIHRvIGdldCBhCndhcm5pbmcgdGhhdCB0aGUgY2hhaW5zIGFyZSBub3QgbWl4aW5nLgoKVGhlIGZvbGxvd2luZyBwbG90IHNob3dzIGFuIGV4YW1wbGUgb2YgdHJhY2UgcGxvdCB3aGVuIG9uZSBjaGFpbgppcyBzdHVjayBpbiBvbmUgbW9kZSBhbmQgdGhyZWUgaW4gYW5vdGhlciBtb2RlLgoKYGBgYHtyIGVjaG89RkFMU0UsIGZpZy5kaW09Yyg5LDMpfQpsb2FkKCdlc3NfZGYyLlJkYXRhJykKbWNtY190cmFjZShkcmF3cykKYGBgYAoKCklmIHdlIHdvdWxkIHJlcGVhdCB0aGUgc2FtcGxpbmcgd2l0aCByYW5kb20gaW5pdGlhbGl6YXRpb25zIGFuZCBmb3VyCmNoYWlucywgd2Ugd291bGQgb2JzZXJ2ZSAwLS00IGNoYWlucyBzdHVjayBpbiBvbmUgbW9kZSBhbmQgdGhlIHJlc3QKaW4gdGhlIG90aGVyIG1vZGUuIFNvIHRoZXJlIGlzIHN0aWxsIGEgcG9zc2liaWxpdHkgdGhhdCBhbGwgdGhlCmNoYWlucyBhcmUgc3R1Y2sgaW4gb25lIG1vZGUgYW5kIHdlIHRoaW5rIHRoYXQgdGhlIHBvc3RlcmlvciBpcwp1bmltb2RhbC4gV2l0aCBqdXN0IG9uZSBjaGFpbiwgd2UgaGF2ZSBwcm9iYWJpbGl0eSBvZiAwIHJlY29nbml6aW5nCnRoYXQgdGhlcmUgYXJlIHdlbGwgc2VwYXJhdGVkIG1vZGVzLiBXaXRoIG1vcmUgY2hhaW5zLCBpdCBpcwpwb3NzaWJsZSB0aGF0IG91ciByYW5kb20gaW5pdGlhbGl6YXRpb24gaXMgd2l0aGluIHRoZSBhdHRyYWN0aW9uCmFyZWEgb2YganVzdCBvbmUgbW9kZSwgYW5kIHRoZW4gaGF2aW5nIG1vcmUgY2hhaW5zIGRvZXNuJ3QgaGVscC4gSW4KZ2VuZXJhbCwgd2l0aCBpbmNyZWFzaW5nIG51bWJlciBvZiBjaGFpbnMsIHdlIGhhdmUgaGlnaGVyCnByb2JhYmlsaXR5IG9mIHNlZWluZyBtb3JlIHRoYW4gb25lIG1vZGUuCgpIb3cgc2hvdWxkIEVTUyBlc3RpbWF0ZXMgbG9vayBsaWtlIHdoZW4gd2UgaGF2ZSBjaGFpbnMgdGhhdCBhcmUgbm90Cm1peGluZz8gSWYgdGhlIG1vZGVzIGFyZSB3ZWxsIHNlcGFyYXRlZCBzbyB0aGF0IGJldHdlZW4tY2hhaW4KdmFyaWFuY2UgaXMgbXVjaCBiaWdnZXIgdGhhbiB3aXRoaW4tY2hhaW4gdmFyaWFuY2VzLCBlYWNoIGNoYWluIGlzCm1vc3RseSBpbmRpY2F0aW5nIHRoZSBsb2FjdGlvbiBvZiB0aGUgbW9kZSBhbmQgbm90IHdvcnRoIG11Y2ggbW9yZQp0aGFuIG9uZSBvYnNlcnZhdGlvbi4gIFRoZSBgcG9zdGVyaW9yYCBwYWNrYWdlIGNvbWJpbmVzIEdleWVyCnRydW5jYXRlZCBhdXRvY29ycmVsYXRpb24gZXN0aW1hdGVzIHdpdGggbXVsdGktY2hhaW4gY29udmVyZ2VuY2UKZGlhZ25vc3RpYyAkXHdpZGVoYXR7Un0kLiBJZiB0aGUgY2hhaW5zIGFyZSBub3QgbWl4aW5nLCB0aGVuIHRoZQpkcmF3cyB3aXRoaW4gZWFjaCBjaGFpbiBhcmUgY29uc2lkZXJlZCB0byBiZSBtb3JlIGNvcnJlbGF0ZWQgd2l0aGluCnRoZSBjaGFpbiBhbmQgdGhlIG11bHRpLWNoYWluIEVTUyBlc3RpbWF0ZSBnZXRzIHNtYWxsZXIuIElmIHRoZQptb2RlcyBhcmUgd2VsbCBzZXBhcmF0ZWQsIG11bHRpLWNoYWluIEVTUyBpcyBjbG9zZSB0byB0aGUgbnVtYmVyIG9mCmNoYWlucy4gQXJndWFibHkgZXZlbiB0aGlzIGlzIGFuIG92ZXJlc3RpbWF0ZSBhcyBpbmNyZWFzaW5nIHRoZQpudW1iZXIgb2Ygbm9uLW1peGluZyBjaGFpbnMgZG9lcyBub3QgcHJvdmlkZSByZWxpYWJsZSBlc3RpbWF0ZSBvZgp0aGUgcmVsYXRpdmUgbWFzc2VzIG9mIHRoZSBtYXNzZXMuCgpUaGUgZm9sbG93aW5nIHBsb3Qgc2hvd3MgRVNTIHBlciBjaGFpbiBlc3RpbWF0ZXMgdXNpbmcgdGhlIGZvdXIKcGFja2FnZXMgYW5kIHdpdGggaW5jcmVhc2luZyBudW1iZXIgb2YgY2hhaW5zLiBTaW11bGF0aW9ucyBoYXZlCmJlZW4gcmVwZWF0ZWQgMTAwIHRpbWVzLgoKCmBgYGB7ciBlY2hvPUZBTFNFLCBmaWcuZGltPWMoOSw0KX0KbG9hZCgnZXNzX2RmMi5SZGF0YScpCmdncGxvdChkZjIsIGFlcyh4PWxvZzIoY2hhaW5zKSwKICAgICAgICAgICAgICAgIHk9ZXNzL2NoYWlucywgY29sb3I9ZmFjdG9yKG1ldGhvZCkpKSArCiAgZ2VvbV9qaXR0ZXIod2lkdGg9MC4wNyxoZWlnaHQ9MCxhbHBoYT0xLHNoYXBlPTEpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAxLCBsaW5ldHlwZT0nZGFzaGVkJykgKwogIGxhYnMoeD0nTnVtYmVyIG9mIGNoYWlucycsIHk9J0VzdGltYXRlZCBFU1MgcGVyIGNoYWluJykrCiAgZ3VpZGVzKGNvbG9yID0gIm5vbmUiKSsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzPTA6NCwgbGFiZWxzID0gYygxLDIsNCw4LDE2KSkrCiAgc2NhbGVfeV9jb250aW51b3VzKHRyYW5zPSdsb2cxMCcpKwogIHNjYWxlX2NvbG9yX2JyaWdodChicmVha3M9YygiMSIsIjIiLCIzIiwiNCIpLCBsYWJlbHM9Yygnc3RhYmxlR1InLCdtY21jc2UnLCdjb2RhJywncG9zdGVyaW9yJykpKwogIGZhY2V0X2dyaWQoY29scz12YXJzKG1ldGhvZGxhYikpCmBgYGAKCgpXaXRoIG9uZSBjaGFpbiwgYWxsIHBhY2thZ2VzIGNvbnNpZGVyIG9ubHkgdGhlIHdpdGhpbi1jaGFpbgpjb3JyZWxhdGlvbiBhbmQgcmVwb3J0IHNpbWlsYXIgRVNTIHBlciBjaGFpbiB2YWx1ZXMgKH4zNTApLiBXaGVuCmRpZmZlcmVudCBjaGFpbnMgZW5kIHVwIHRvIHN0dWNrIGluIGRpZmZlcmVudCB3ZWxsLXNlcGFyYXRlZCBtb2RlcywKd2Ugd291bGQgcHJlZmVyIHRvIGdldCBhbiBlc3RpbWF0ZSB0aGF0IGlzIGluZGljYXRpbmcgcG9vcgplZmZpY2llbmN5LiBXaXRoIGluY3JlYXNpbmcgbnVtYmVyIG9mIGNoYWlucywgYGNvZGFgIGtlZXBzCnJlcG9ydGluZyBzaW1pbGFyIEVTUyBwZXIgY2hhaW4gKHdpdGggbGVzcyB2YXJpYXRpb24pIGFuZCBwcm92aWRlcwpubyBpbmRpY2F0aW9uIG9mIG5vbi1taXhpbmcgY2hhaW5zLiAgV2hlbiBhdCBsZWFzdCBvbiBjaGFpbiBpcwpzdHVjayBpbiBhIGRpZmZlcmVudCBtb2RlLCBgc3RhYmxlR1JgIHJlcG9ydHMgRVNTIHBlciBjaGFpbiBvZgphcHByb3hpbWF0ZWx5IDYwLCB3aGljaCBpcyBtdWNoIGxlc3MgdGhhbiAzNTAsIGJ1dCB3ZSBjb3VsZCBzZWUKc2ltaWxhciBsb3cgdmFsdWVzIGR1ZSB0byBoaWdoIGF1dG9jb3JyZWxhdGlvbiBhbmQgdGhlIHRvdGFsIEVTUyBpcwpoaWdoIGVub3VnaCBub3QgdG8gaW5kaWNhdGUgdGhhdCBjaGFpbnMgYXJlIG5vdCBtaXhpbmcKd2VsbC4gYG1jbWNzZWAgZG9lcyBub3QgaGF2ZSBzdXBwb3J0IGZvciBtdWx0aXBsZSBjaGFpbnMsIGJ1dApjb21iaW5pbmcgdGhlIGNoYWlucyB0byBvbmUgY2hhaW4sIGNoYW5nZXMgdGhlIGF1dG9jb3JyZWxhdGlvbgplc3RpbWF0ZSBzbyB0aGF0IHdoZW4gdGhlIG51bWJlciBvZiBjaGFpbnMgaW5jcmVhc2VzLCBldmVudHVhbGx5CkVTUyBwZXIgY2hhaW4gZGVjcmVhc2VzIGNsb3NlIHRvIDEsIGluZGljYXRpbmcgaW5lZmZpY2llbnQKc2FtcGxpbmcuICBgcG9zdGVyaW9yYCByZXBvcnRzIEVTUyBwZXIgY2hhaW4gY2xvc2UgdG8gMSBhbHdheXMgd2hlbgphdCBsZWFzdCBvbmUgY2hhaW4gaXMgaW4gdGhlIG90aGVyIG1vZGUsIHdoaWNoIGlzIGEgY2xlYXIKaW5kaWNhdGlvbiB0aGF0IHNvbWV0aGluZyBpcyB3cm9uZyBpbiB0aGUgbWl4aW5nIG9mIHRoZSBjaGFpbnMuCgojIyBSZWZlcmVuY2VzCgotIEdleWVyLCBDLiBKLiAoMTk5MikuIFByYWN0aWNhbCBNYXJrb3YgY2hhaW4gTW9udGUgQ2FybG8uICpTdGF0aXN0aWNhbCBTY2llbmNlKiwgNzo0NzPigJM0ODMuCi0gR29uZywgTC4sIGFuZCBGbGVnYWwsIEouIE0uICgyMDE1KS4gQSBwcmFjdGljYWwgc2VxdWVudGlhbCBzdG9wcGluZyBydWxlIGZvciBoaWdoLWRpbWVuc2lvbmFsIE1hcmtvdiBjaGFpbiBNb250ZSBDYXJsby4gKkpvdXJuYWwgb2YgQ29tcHV0YXRpb25hbCBhbmQgR3JhcGhpY2FsIFN0YXRpc3RpY3MqLCAyNTo2ODTigJQ3MDAuCi0gSGVpZGVsYmVyZ2VyLCBQLiwgYW5kIFdlbGNoLCBQLiBELiAoMTk4MSkuIEEgc3BlY3RyYWwgbWV0aG9kIGZvciBjb25maWRlbmNlIGludGVydmFsIGdlbmVyYXRpb24gYW5kIHJ1biBsZW5ndGggY29udHJvbCBpbiBzaW11bGF0aW9ucy4gKkNvbW11bmljYXRpb25zIG9mIHRoZSBBQ00qLCAyNDoyMzPigJQyNDUuCi0gVmF0cywgRC4sIGFuZCBLbnVkc29uLCBDLiAoMjAxOCkuIFJldmlzaXRpbmcgdGhlIEdlbG1hbi1SdWJpbiBkaWFnbm9zdGljLiAqYXJYaXYgcHJlcHJpbnQqLCBhclhpdjoxODEyLjA5Mzg0LgotIFZhdHMsIEQuLCBhbmQgS251ZHNvbiwgQy4gKDIwMjEpLiBSZXZpc2l0aW5nIHRoZSBHZWxtYW4tUnViaW4gZGlhZ25vc3RpYy4gKlN0YXRpc3RpY2FsIFNjaWVuY2UqLCAzNig0KTogNTE44oCUNTI5LgotIFZlaHRhcmksIEEuLCBHZWxtYW4sIEEuLCBTaW1wc29uLCBELiwgQ2FycGVudGVyLCBCLiwgQsO8cmtuZXIsIFAuLUMuICgyMDIxKTogUmFuay1ub3JtYWxpemF0aW9uLCBmb2xkaW5nLCBhbmQgbG9jYWxpemF0aW9uOiBBbiBpbXByb3ZlZCAkXHdpZGVoYXR7Un0kIGZvciBhc3Nlc3NpbmcgY29udmVyZ2VuY2Ugb2YgTUNNQy4gKkJheWVzaWFuIGFuYWx5c2lzKiwgMTYoMik6NjY34oCUNzE4LiBbZG9pOjEwLjEyMTQvMjAtQkExMjIxXShodHRwczovL2RvaS5vcmcvMTAuMTIxNC8yMC1CQTEyMjEpLgoK