As Pdf

All traceplots (and other plots) are subsampled to keep this file below 100MB; rejection plateaus can’t be seen in traceplots

1 Changelog

  • Fixed: lwidth, gwidth prior is not approx. \(\frac{1}{x}\) but essentially a positive constant prior for reasonable values of lwidth, gwidth.
  • Fixed: initial.fit.EM is not MAP; if anything it’s MLE but it’s not even that, it is more like a heuristic / Blackbox.
  • Added: How prop.NormPropToWidth calculates the proposal in Appendix D.

2 Generate Synthetic Data

Simple synthetic signal has 4 peaks. Plotted later.

library("spectralmc")

# get synthetic data
data <- data.synthetic.stormyclouds(seed = 11, kp = 4, noise = 0.001)
x <- data$x
y <- data$y

3 Stochastic Model

Highly uninformative priors, uniform prior for position pos and a essentially positive constant prior for lwidth and gwidth. Improper prior for amplitude amp, forces amp to be positive. Noise of the signal, noise_sigma, is assumed to be known and set to the true noise sigma present in the data. Framework allows to easily model noise_sigma as stochastic variable aswell, not done here.

logprior <- LogPrior(
  amp = prior.Positive(),
  lwidth = prior.Gamma(1.01, 0.01), #shape rate
  gwidth = prior.Gamma(1.01, 0.01), #shape rate
  pos = prior.Unif(0, 1000), # min, max
  a = prior.Norm(0, 0.00001), # mean, sd
  b = prior.Unif(-1000, 1000)
)

fit_kp <- 4 # number of peaks to fit
signal_model <- voigt.model

posterior <- Posterior(Loglikeli(signal_model, noise_sigma = 0.001), logprior)

4 MH - Perfect Start

Starting point of the chain is calculated with a heuristic based on the expectation-maximization algorithm. Essentially a blackbox for now. Proposal for metropolis hastings (mhmc) is a simple normal distribution with a diagonal covariance matrix. Implemented by sampling independently for every variable type such amp, pos and b. See Proposal in code. The mhmc algorithm is run for 40000 iterations.

good_start <- initial.fit.EM(x, y, fit_kp, seed = 43423)

  |================================================================================| 100% elap:  1s eta:  0s
proposal <- Proposal(
  is_symmetric = TRUE, #flag proposal as symmetric; speeds up chains.mhmc
  amp = prop.Norm(0.0005), #sd
  lwidth = prop.Norm(0.003),
  gwidth = prop.Norm(0.003), 
  pos = prop.Norm(0.0005),
  a = prop.Norm(0.00000005),
  b = prop.Norm(0.00002) 
)
res <- chains.mhmc(x, y, good_start, posterior, proposal, iter = 40000, print_bar = FALSE)

  |================================================================================| 100% elap: 20s eta:  0s

4.1 Special Diagnostics for high kp:

4.1.1 plt.fit.chain.interactive

Plots for each iteration the fit induced by the sample. If step_width is specified it only plots iterations which are multiples of step_width. Allows to understand why a chain gets stuck (for example 2 peaks fitted as one), identify missing peaks and visualize the burn-in process.

plt.fit.chain.interactive(x, y, res$samples, signal_model, step_width = 4000)

4.1.2 plt.traceplot

Groups traceplots for different peaks by parameter type. While the normal coda plots are needed for publications, there are simply to many plots (100+) when fitting many peaks.

Usage Example: We want to figure out the lwidth of the peak at around pos = 486. First we plot the `pos traceplot, quickly we can then identify the peak around pos = 486 to be pos[1]. Zoom to magnify

plt.traceplot(res$samples, "pos", steps=2000)
Now that we know that the peak is 1 we can look at the lwidth plot, and extract the lwidth[1] trace by double clicking the legend. Thus we can read of the lwidth to be 1.08. This is near impossible to do with coda plots for a high number of peaks.
plt.traceplot(res$samples, "lwidth", steps=2000)

All other traceplots are in the Appendix A

4.1.3 plt.posterior

Another very useful diagnostic is the posterior plot, which plots the logposterior value of the i-th sample. This diagnostic should be roughly monotonically increasing during burn-in and flat afterwards. It is a computational cheap diagnostic that generalizes to a high number of peaks as well. In the plot below (zoom in on the top) one can see that the chain is still not burned-in, as the posterior is not flat. This is in fact reflected in a slow drift of lwidths which are still quite wrong for the wide peaks. This is not trivial to see using traceplots and other diagnostics. Zoom to magnify

plt.posterior(res$post_vals, steps = 1000)

4.1.4 plt.rolling_acceptions

We might also be interested in the acceptance rate. In the case that our chain hasn’t fully burned in yet a rolling mean / windowed version of the acceptance rate is useful.

plt.rolling_acceptions(res$acceptions, windowsize = 500, steps = 1000)
res_MHMC1 <- res # save for appendix

4.2 Problems for MH - Perfect Start:

Finding a good proposal distribution, such as the one above, takes a lot of hyperparameter tuning.

4.2.1 Different Peak Widths

The thinner and higher a peak is the preciser its pos, lwidth and gwidth becomes. Thus thinner peaks need finer proposals. The traceplots (shown above) for the thinnest peak are very good (pos[1], lwidth[1], gwidth[1]) but the proposal is too fine for the other peaks (any other trace). Hence there is drift in their traces (i.e. pos[2]). Due to the thin peak a coarser proposal yields a massive drop in the acceptance rate and isn’t a viable solution to the problem either. Consequently there is no way to find hyperparameters that have high acceptance and a low drift in the traces.

One simple fix that gets around this problem is to have an asymmetric proposal for which the standard deviation (sd) of the pos, gwidth and lwidth proposal is given by sd <- unit_sd * sqrt(gwidth^2 + lwidth^2)/unit_width (with hyperparameters unit_sd and unit_width) (implementation in Appendix D). Thus wider peaks have a bigger sd and thinner peaks a smaller sd. This proposal actually is rather important and fixes issues that occur in the burn-in of the chain and is therefore also used in the following section ‘Burn-in Problems’.

proposal2 <- Proposal(
  is_symmetric = FALSE,
  amp = prop.Norm(0.0005), #sd
  lwidth = prop.NormPropToWidth(0.003, 1.4),
  gwidth = prop.NormPropToWidth(0.003, 1.4), # unit_sd, unit_width. Such that for a peak of width = unit_width the sd is unit_sd. Scales linearly for different peak widths.
  pos = prop.NormPropToWidth(0.0005, 1.4),
  a = prop.Norm(0.00000005),
  b = prop.Norm(0.00002) #b = prop.Unif(0.02)
)

res <- chains.mhmc(x, y, good_start, posterior, proposal2, iter = 40000, print_bar = FALSE)

  |================================================================================| 100% elap: 25s eta:  0s

The resulting chain is far better, which becomes obvious when looking at the taceplot for lwidth and comparing it to the traceplot for lwidth using an symmetric proposal (here). All other diagnostics in the Appendix B

# Compare this to previous 'Trace plot of lwidth'
plt.traceplot(res$samples, "lwidth", steps=2000)
res_MHMC2 <- res #save for appendix

4.2.2 Saddle Points / Modes

Below is a chain shown that is stuck in a local max / saddle point. Why is it stuck? The starting point is already burned-in as it is a sample from the burned-in chain from the following section ‘Burn-in Problems’. Obtaining this burned-in chain takes roughly a million iterations and 5 different proposals. The traceplots and posterior plots are flat which heavily suggests that the chain is indeed burned-in, this too confirms the assumption that the starting point is already burned in.

However if this is the case, the chain is stuck in a quite bad local max / saddle point, only fitting 3 peaks. As shown below, the chain can’t leave the saddle point even after 300k iterations. There is no way to fix this trivially with a local method (any local proposal) as this is an innate property of the posterior.

More precisely, the reason for this issue here is that there is a misplaced peak (pos[3]) instead of it being at the location of the first twin peaks it is located in a flat region of the signal.

This might be a saddle point and resolve itself after a few million iterations. However it is in-feasible to calculate so many iterations.

start <- list(
  pos = c(139.4636, 486.4108, 873.3819, 419.1684),
  gwidth = c(25.5351788, 0.9830332, 266.8899655, 2.7727781),
  lwidth = c(0.05998143, 1.08466880, 25.27266471, 9.86956400),
  amp = c(2.0895949, 2.8062770, 0.8353660, 0.5985949),
  a = -2.748646e-06,
  b = 20.01264
)

# non symmetric proposal to allow for better position diffusion
proposal2 <- Proposal(
  is_symmetric = FALSE,
  amp = prop.Norm(0.0005),
  lwidth = prop.NormPropToWidth(0.003, 1.4),
  gwidth = prop.NormPropToWidth(0.003, 1.4), # unit_sd, unit_width. Such that for a peak of width = unit_width the sd is unit_sd. Scales linearly for different peak widths.
  pos = prop.NormPropToWidth(0.0005, 1.4),
  a = prop.Norm(0.00000005),
  b = prop.Norm(0.00002)
)

res <- chains.mhmc(x, y, start, posterior, proposal2, iter = 300000, print_bar = FALSE)

  |================================================================================| 100% elap:  5m eta:  0s

In the following plots we can see how it fails to fit the twin peaks, and in the traceplot of pos the missing/misplaced peak can be found at pos approx. 900. All other diagnostics are in the Appendix C

plt.fit.chain.interactive(x, y, res$samples, signal_model, step_width = 60000)
plt.traceplot(res$samples, "pos", steps = 2000)
res_MHMC3 <- res

5 MH - Random Start

5.1 Burn-in Problems

Major burn-in problems when starting from an problem agnostic starting point that is random (prop.voigt.rnd_start). A coarse proposal is needed for a fast burn-in. This however causes a tiny acceptance rate once burned in. Thus different proposals for the burn-in and burned-in state are used. If we were to use the fine proposal from the previous sections the burn-in is 10+ million iterations long. Instead we start with a very coarse proposal and make it every few 10000 iterations finer. Clearly this is quite messy and very time intensive as one has to tune, in this case, 5 proposals.

In the following very long burn-in code, a few interesting things occur. First a peak (peak 3) moves in a terrible position due to a odd saddle point in the posterior, this can be seen very obviously in the second interactive fit plots. Secondly, in the early stages of the burn-in two peaks fit the large peak with exactly the same position. It takes 400k iterations with the special asymmetric proposal to pull them apart and to get one of them to fit the small peak next to the big one.

start <- prop.voigt.rnd_start(343, fit_kp)

# first proposal, burns-in b.
b_burnin <- Proposal(
  is_symmetric = TRUE,
  amp = prop.Norm(0.0005),
  lwidth = prop.Norm(0.003),
  gwidth = prop.Norm(0.003),
  pos = prop.Norm(0.005),
  a = prop.Norm(0.00000005),
  b = prop.Norm(0.2)
)
res <- chains.mhmc(x, y, start, posterior, b_burnin, iter = 1000, print_bar = FALSE)

  |================================================================================| 100% elap:  1s eta:  0s
plt.fit.chain.interactive(x, y, res$samples, signal_model, step_width = 200)
start <- res$samples[[length(res$samples)]]


# second proposal, burns-in pos. interestingly as plt.fit.chain.interactive shows one peak moves into the the wrong direction.
# This is due to the non-trivial local-maxima / saddle point structure of the posterior.
pos_burnin <- Proposal(
  is_symmetric = TRUE,
  amp = prop.Norm(0.0005),
  lwidth = prop.Norm(0.003),
  gwidth = prop.Norm(0.003),
  pos = prop.Norm(0.5),
  a = prop.Norm(0.00000005),
  b = prop.Norm(0.00002)
)
res <- chains.mhmc(x, y, start, posterior, pos_burnin, iter = 20000, print_bar = FALSE)

  |================================================================================| 100% elap: 20s eta:  0s
plt.fit.chain.interactive(x, y, res$samples, signal_model, step_width = 1000)
start <- res$samples[[length(res$samples)]]

# burn in for widths
width_burnin <- Proposal(
  is_symmetric = TRUE,
  amp = prop.Norm(0.0005),
  lwidth = prop.Norm(0.009),
  gwidth = prop.Norm(0.009),
  pos = prop.Norm(0.04),
  a = prop.Norm(0.00000005),
  b = prop.Norm(0.00002)
)
res <- chains.mhmc(x, y, start, posterior, width_burnin, iter = 20000, print_bar = FALSE)

  |================================================================================| 100% elap: 18s eta:  0s
plt.fit.chain.interactive(x, y, res$samples, signal_model, step_width = 1000)
start <- res$samples[[length(res$samples)]]

# next burn in
next_burnin <- Proposal(
  is_symmetric = TRUE,
  amp = prop.Norm(0.0005), #sd
  lwidth = prop.Norm(0.009),
  gwidth = prop.Norm(0.009), # can also use prop.Unif(radius) for uniform proposal
  pos = prop.Norm(0.04),
  a = prop.Norm(0.00000005),
  b = prop.Norm(0.00002) #b = prop.Unif(0.02)
)
res <- chains.mhmc(x, y, start, posterior, next_burnin, iter = 100000, print_bar = FALSE)

  |================================================================================| 100% elap:  1m eta:  0s
plt.fit.chain.interactive(x, y, res$samples, signal_model, step_width = 5000)
start <- res$samples[[length(res$samples)]]

asym_burnin <- Proposal(
  is_symmetric = FALSE,
  amp = prop.Norm(0.0005),
  lwidth = prop.NormPropToWidth(0.003, 1.4),
  gwidth = prop.NormPropToWidth(0.003, 1.4), # unit_sd, unit_width. Such that for a peak of width = unit_width the sd is unit_sd. Scales linearly for different peak widths.
  pos = prop.NormPropToWidth(0.0005, 1.4),
  a = prop.Norm(0.00000005),
  b = prop.Norm(0.00002)
)
# almost final burn in (please)
res <- chains.mhmc(x, y, start, posterior, asym_burnin, iter = 150000, print_bar = FALSE)

  |================================================================================| 100% elap:  2m eta:  0s
plt.fit.chain.interactive(x, y, res$samples, signal_model, step_width = 5000)
start <- res$samples[[length(res$samples)]]

# can't be burned in yet as posterior isn't flat
plt.posterior(res$post_vals)
# non symmetric proposal to allow for better position diffusion
proposal2 <- Proposal(
  is_symmetric = FALSE,
  amp = prop.Norm(0.0005),
  lwidth = prop.NormPropToWidth(0.003, 1.4),
  gwidth = prop.NormPropToWidth(0.003, 1.4), # unit_sd, unit_width. Such that for a peak of width = unit_width the sd is unit_sd. Scales linearly for different peak widths.
  pos = prop.NormPropToWidth(0.0005, 1.4),
  a = prop.Norm(0.00000005),
  b = prop.Norm(0.00002)
)

start <- res$samples[[length(res$samples)]]
res <- chains.mhmc(x, y, start, posterior, proposal2, iter = 300000, print_bar = FALSE)

  |================================================================================| 100% elap:  5m eta:  0s
plt.fit.chain.interactive(x, y, res$samples, signal_model, step_width = 60000)
plt.traceplot(res$samples, "pos", steps = 2000)
plt.traceplot(res$samples, "amp", steps = 2000)
plt.traceplot(res$samples, "gwidth", steps = 2000)
plt.traceplot(res$samples, "lwidth", steps = 2000)
plt.posterior(res$post_vals, steps = 1000)
plt.rolling_acceptions(res$acceptions, windowsize = 1000, steps = 1000)

6 Appendix

6.1 Appendix A

Plots for the first Chain.

plt.traceplot(res_MHMC1$samples, "gwidth", steps=2000)
plt.traceplot(res_MHMC1$samples, "amp", steps=2000)
plt.traceplot(res_MHMC1$samples, "a", steps=2000)
plt.traceplot(res_MHMC1$samples, "b", steps=2000)

6.2 Appendix B

Plots for the second Chain.

plt.fit.chain.interactive(x, y, res_MHMC2$samples, signal_model, step_width = 5000)
plt.traceplot(res_MHMC2$samples, "pos", steps=2000)
plt.traceplot(res_MHMC2$samples, "gwidth", steps=2000)
plt.traceplot(res_MHMC2$samples, "amp", steps=2000)
plt.traceplot(res_MHMC2$samples, "a", steps=2000)
plt.traceplot(res_MHMC2$samples, "b", steps=2000)
plt.posterior(res_MHMC2$post_vals, steps = 1000)
plt.rolling_acceptions(res_MHMC2$acceptions, windowsize = 1000, steps = 1000)

6.3 Appendix C

plt.fit.chain.interactive(x, y, res_MHMC3$samples, signal_model, step_width = 5000)
plt.traceplot(res_MHMC3$samples, "pos", steps=2000)
plt.traceplot(res_MHMC3$samples, "gwidth", steps=2000)
plt.traceplot(res_MHMC3$samples, "amp", steps=2000)
plt.traceplot(res_MHMC3$samples, "a", steps=2000)
plt.traceplot(res_MHMC3$samples, "b", steps=2000)
plt.posterior(res_MHMC3$post_vals, steps = 1000)
plt.rolling_acceptions(res_MHMC3$acceptions, windowsize = 1000, steps = 1000)

6.4 Appendix D

# prop.NormPropToWidth essentially does, where x could be pos, lwidth or gwidth:
width <- sqrt(params$gwidth^2 + params$lwidth^2)
sd <- width / self$unit_width * self$unit_sd
rnorm(length(x), mean = x, sd)

Ideas: - Change of variables; instead of lwidth, gwidth use FWHM and eta.


  1. RWTH Aachen, ↩︎

  2. This work was developed under a Seed Fund Project (2021) of the RWTH Aachen University, funded under "the Excellence Strategy of the Federal Government and the Länder". Project: "Spectra-Bayes: A Bayesian statistical machine learning model for spectral reconstruction" (Project Leaders: Prof. Dr. Maria Kateri, Prof. Dr.-Ing. Hans-Jürgen Koß) ↩︎

LS0tDQp0aXRsZTogIlByb2JsZW1zIHdpdGggdGhlIE1ldHJvcG9saXMgSGFzdGluZ3MgQWxnb3JpdGhtIg0KYXV0aG9yOiBKYW4gTWVpw59uZXJeW1JXVEggQWFjaGVuLCBwaGlsaXBwLm1laXNzbmVyQHJ3dGgtYWFjaGVuLmRlXQ0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOg0KICAgIHRvYzogdHJ1ZQ0KICAgIHRvY19kZXB0aDogNQ0KICAgIHRvY19mbG9hdDoNCiAgICAgIGNvbGxhcHNlZDogZmFsc2UNCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUNCi0tLQ0KDQo8c2NyaXB0IHR5cGU9InRleHQvamF2YXNjcmlwdCI+DQogIGZ1bmN0aW9uIGp1bXBfaGVhZGVyKGtleSl7DQogICAgJCgnOmhlYWRlcjpjb250YWlucygnK2tleSsnKScpWzBdLnNjcm9sbEludG9WaWV3KCk7DQogIH0NCiAgDQogIGZ1bmN0aW9uIGp1bXBfbWFya2VkKGtleSl7DQogICAgJCgnIycgKyBrZXkpWzBdLnNjcm9sbEludG9WaWV3KCk7DQogIH0NCg0KPC9zY3JpcHQ+DQoNCioqQWxsIHRyYWNlcGxvdHMgKGFuZCBvdGhlciBwbG90cykgYXJlIHN1YnNhbXBsZWQgdG8ga2VlcCB0aGlzIGZpbGUgYmVsb3cgMTAwTUI7IHJlamVjdGlvbiBwbGF0ZWF1cyBjYW4ndCBiZSBzZWVuIGluIHRyYWNlcGxvdHMqKg0KDQojIENoYW5nZWxvZw0KDQotIEZpeGVkOiBgbHdpZHRoYCwgYGd3aWR0aGAgcHJpb3IgaXMgbm90IGFwcHJveC4gJFxmcmFjezF9e3h9JCBidXQgZXNzZW50aWFsbHkgYSBwb3NpdGl2ZSBjb25zdGFudCBwcmlvciBmb3IgcmVhc29uYWJsZSB2YWx1ZXMgb2YgYGx3aWR0aGAsIGBnd2lkdGhgLg0KLSBGaXhlZDogYGluaXRpYWwuZml0LkVNYCBpcyBub3QgTUFQOyBpZiBhbnl0aGluZyBpdCdzIE1MRSBidXQgaXQncyBub3QgZXZlbiB0aGF0LCBpdCBpcyBtb3JlIGxpa2UgYSBoZXVyaXN0aWMgLyBCbGFja2JveC4NCi0gQWRkZWQ6IEhvdyBgcHJvcC5Ob3JtUHJvcFRvV2lkdGhgIGNhbGN1bGF0ZXMgdGhlIHByb3Bvc2FsIGluIEFwcGVuZGl4IEQuDQoNCiMgR2VuZXJhdGUgU3ludGhldGljIERhdGENCg0KU2ltcGxlIHN5bnRoZXRpYyBzaWduYWwgaGFzIDQgcGVha3MuIFBsb3R0ZWQgbGF0ZXIuDQpgYGB7cn0NCmxpYnJhcnkoInNwZWN0cmFsbWMiKQ0KDQojIGdldCBzeW50aGV0aWMgZGF0YQ0KZGF0YSA8LSBkYXRhLnN5bnRoZXRpYy5zdG9ybXljbG91ZHMoc2VlZCA9IDExLCBrcCA9IDQsIG5vaXNlID0gMC4wMDEpDQp4IDwtIGRhdGEkeA0KeSA8LSBkYXRhJHkNCmBgYA0KDQojIFN0b2NoYXN0aWMgTW9kZWwNCg0KSGlnaGx5IHVuaW5mb3JtYXRpdmUgcHJpb3JzLCB1bmlmb3JtIHByaW9yIGZvciBwb3NpdGlvbiBgcG9zYCBhbmQgYSBlc3NlbnRpYWxseSBwb3NpdGl2ZSBjb25zdGFudCBwcmlvciBmb3IgYGx3aWR0aGAgYW5kIGBnd2lkdGhgLiBJbXByb3BlciBwcmlvciBmb3IgYW1wbGl0dWRlIGBhbXBgLCBmb3JjZXMgYGFtcGAgdG8gYmUgcG9zaXRpdmUuIE5vaXNlIG9mIHRoZSBzaWduYWwsIGBub2lzZV9zaWdtYWAsIGlzIGFzc3VtZWQgdG8gYmUga25vd24gYW5kIHNldCB0byB0aGUgdHJ1ZSBub2lzZSBzaWdtYSBwcmVzZW50IGluIHRoZSBkYXRhLiBGcmFtZXdvcmsgYWxsb3dzIHRvIGVhc2lseSBtb2RlbCBgbm9pc2Vfc2lnbWFgIGFzIHN0b2NoYXN0aWMgdmFyaWFibGUgYXN3ZWxsLCBub3QgZG9uZSBoZXJlLg0KYGBge3J9DQpsb2dwcmlvciA8LSBMb2dQcmlvcigNCiAgYW1wID0gcHJpb3IuUG9zaXRpdmUoKSwNCiAgbHdpZHRoID0gcHJpb3IuR2FtbWEoMS4wMSwgMC4wMSksICNzaGFwZSByYXRlDQogIGd3aWR0aCA9IHByaW9yLkdhbW1hKDEuMDEsIDAuMDEpLCAjc2hhcGUgcmF0ZQ0KICBwb3MgPSBwcmlvci5VbmlmKDAsIDEwMDApLCAjIG1pbiwgbWF4DQogIGEgPSBwcmlvci5Ob3JtKDAsIDAuMDAwMDEpLCAjIG1lYW4sIHNkDQogIGIgPSBwcmlvci5VbmlmKC0xMDAwLCAxMDAwKQ0KKQ0KDQpmaXRfa3AgPC0gNCAjIG51bWJlciBvZiBwZWFrcyB0byBmaXQNCnNpZ25hbF9tb2RlbCA8LSB2b2lndC5tb2RlbA0KDQpwb3N0ZXJpb3IgPC0gUG9zdGVyaW9yKExvZ2xpa2VsaShzaWduYWxfbW9kZWwsIG5vaXNlX3NpZ21hID0gMC4wMDEpLCBsb2dwcmlvcikNCiAgDQpgYGANCg0KIyBNSCAtIFBlcmZlY3QgU3RhcnQNCg0KU3RhcnRpbmcgcG9pbnQgb2YgdGhlIGNoYWluIGlzIGNhbGN1bGF0ZWQgd2l0aCBhIGhldXJpc3RpYyBiYXNlZCBvbiB0aGUgZXhwZWN0YXRpb24tbWF4aW1pemF0aW9uIGFsZ29yaXRobS4gRXNzZW50aWFsbHkgYSBibGFja2JveCBmb3Igbm93LiBQcm9wb3NhbCBmb3IgbWV0cm9wb2xpcyBoYXN0aW5ncyAobWhtYykgaXMgYSBzaW1wbGUgbm9ybWFsIGRpc3RyaWJ1dGlvbiB3aXRoIGEgZGlhZ29uYWwgY292YXJpYW5jZSBtYXRyaXguIEltcGxlbWVudGVkIGJ5IHNhbXBsaW5nIGluZGVwZW5kZW50bHkgZm9yIGV2ZXJ5IHZhcmlhYmxlIHR5cGUgc3VjaCBgYW1wYCwgYHBvc2AgYW5kIGBiYC4gU2VlIGBQcm9wb3NhbGAgaW4gY29kZS4gVGhlIG1obWMgYWxnb3JpdGhtIGlzIHJ1biBmb3IgNDAwMDAgaXRlcmF0aW9ucy4NCmBgYHtyfQ0KZ29vZF9zdGFydCA8LSBpbml0aWFsLmZpdC5FTSh4LCB5LCBmaXRfa3AsIHNlZWQgPSA0MzQyMykNCg0KcHJvcG9zYWwgPC0gUHJvcG9zYWwoDQogIGlzX3N5bW1ldHJpYyA9IFRSVUUsICNmbGFnIHByb3Bvc2FsIGFzIHN5bW1ldHJpYzsgc3BlZWRzIHVwIGNoYWlucy5taG1jDQogIGFtcCA9IHByb3AuTm9ybSgwLjAwMDUpLCAjc2QNCiAgbHdpZHRoID0gcHJvcC5Ob3JtKDAuMDAzKSwNCiAgZ3dpZHRoID0gcHJvcC5Ob3JtKDAuMDAzKSwgDQogIHBvcyA9IHByb3AuTm9ybSgwLjAwMDUpLA0KICBhID0gcHJvcC5Ob3JtKDAuMDAwMDAwMDUpLA0KICBiID0gcHJvcC5Ob3JtKDAuMDAwMDIpIA0KKQ0KcmVzIDwtIGNoYWlucy5taG1jKHgsIHksIGdvb2Rfc3RhcnQsIHBvc3RlcmlvciwgcHJvcG9zYWwsIGl0ZXIgPSA0MDAwMCwgcHJpbnRfYmFyID0gRkFMU0UpDQpgYGANCg0KIyMgU3BlY2lhbCBEaWFnbm9zdGljcyBmb3IgaGlnaCBrcDoNCiMjIyBgcGx0LmZpdC5jaGFpbi5pbnRlcmFjdGl2ZWANClBsb3RzIGZvciBlYWNoIGl0ZXJhdGlvbiB0aGUgZml0IGluZHVjZWQgYnkgdGhlIHNhbXBsZS4gSWYgYHN0ZXBfd2lkdGhgIGlzIHNwZWNpZmllZCBpdCBvbmx5IHBsb3RzIGl0ZXJhdGlvbnMgd2hpY2ggYXJlIG11bHRpcGxlcyBvZiBgc3RlcF93aWR0aGAuIEFsbG93cyB0byB1bmRlcnN0YW5kIHdoeSBhIGNoYWluIGdldHMgc3R1Y2sgKGZvciBleGFtcGxlIDIgcGVha3MgZml0dGVkIGFzIG9uZSksIGlkZW50aWZ5IG1pc3NpbmcgcGVha3MgYW5kIHZpc3VhbGl6ZSB0aGUgYnVybi1pbiBwcm9jZXNzLiANCmBgYHtyfQ0KcGx0LmZpdC5jaGFpbi5pbnRlcmFjdGl2ZSh4LCB5LCByZXMkc2FtcGxlcywgc2lnbmFsX21vZGVsLCBzdGVwX3dpZHRoID0gNDAwMCkNCg0KYGBgDQoNCiMjIyBgcGx0LnRyYWNlcGxvdGANCkdyb3VwcyB0cmFjZXBsb3RzIGZvciBkaWZmZXJlbnQgcGVha3MgYnkgcGFyYW1ldGVyIHR5cGUuIFdoaWxlIHRoZSBub3JtYWwgY29kYSBwbG90cyBhcmUgbmVlZGVkIGZvciBwdWJsaWNhdGlvbnMsIHRoZXJlIGFyZSBzaW1wbHkgdG8gbWFueSBwbG90cyAoMTAwKykgd2hlbiBmaXR0aW5nIG1hbnkgcGVha3MuDQoNClVzYWdlIEV4YW1wbGU6IFdlIHdhbnQgdG8gZmlndXJlIG91dCB0aGUgYGx3aWR0aGAgb2YgdGhlIHBlYWsgYXQgYXJvdW5kIGBwb3MgPSA0ODZgLg0KRmlyc3Qgd2UgcGxvdCB0aGUgYGBwb3NgIHRyYWNlcGxvdCwgcXVpY2tseSB3ZSBjYW4gdGhlbiBpZGVudGlmeSB0aGUgcGVhayBhcm91bmQgYHBvcyA9IDQ4NmAgdG8gYmUgYHBvc1sxXWAuDQoqKlpvb20gdG8gbWFnbmlmeSoqDQpgYGB7cn0NCnBsdC50cmFjZXBsb3QocmVzJHNhbXBsZXMsICJwb3MiLCBzdGVwcz0yMDAwKQ0KYGBgDQoNCk5vdyB0aGF0IHdlIGtub3cgdGhhdCB0aGUgcGVhayBpcyBgMWAgd2UgY2FuIGxvb2sgYXQgdGhlIGBsd2lkdGhgIHBsb3QsIGFuZCBleHRyYWN0IHRoZSBgbHdpZHRoWzFdYCB0cmFjZSBieSBkb3VibGUgY2xpY2tpbmcgdGhlIGxlZ2VuZC4NClRodXMgd2UgY2FuIHJlYWQgb2YgdGhlIGBsd2lkdGhgIHRvIGJlIGAxLjA4YC4gVGhpcyBpcyBuZWFyIGltcG9zc2libGUgdG8gZG8gd2l0aCBjb2RhIHBsb3RzIGZvciBhIGhpZ2ggbnVtYmVyIG9mIHBlYWtzLjxkaXYgaWQ9Imx3aWR0aGZpcnN0Y2hhaW4iPjwvZGl2Pg0KYGBge3J9DQpwbHQudHJhY2VwbG90KHJlcyRzYW1wbGVzLCAibHdpZHRoIiwgc3RlcHM9MjAwMCkNCmBgYA0KDQpBbGwgb3RoZXIgdHJhY2VwbG90cyBhcmUgaW4gdGhlIDxhIG9uY2xpY2s9J2p1bXBfaGVhZGVyKCJBcHBlbmRpeCBBIik7Jz5BcHBlbmRpeCBBPC9hPg0KDQojIyMgYHBsdC5wb3N0ZXJpb3JgDQpBbm90aGVyIHZlcnkgdXNlZnVsIGRpYWdub3N0aWMgaXMgdGhlIHBvc3RlcmlvciBwbG90LCB3aGljaCBwbG90cyB0aGUgbG9ncG9zdGVyaW9yIHZhbHVlIG9mIHRoZSBpLXRoIHNhbXBsZS4NClRoaXMgZGlhZ25vc3RpYyBzaG91bGQgYmUgcm91Z2hseSBtb25vdG9uaWNhbGx5IGluY3JlYXNpbmcgZHVyaW5nIGJ1cm4taW4gYW5kIGZsYXQgYWZ0ZXJ3YXJkcy4gSXQgaXMgYSBjb21wdXRhdGlvbmFsIGNoZWFwIGRpYWdub3N0aWMgdGhhdCBnZW5lcmFsaXplcyB0byBhIGhpZ2ggbnVtYmVyIG9mIHBlYWtzIGFzIHdlbGwuDQogIEluIHRoZSBwbG90IGJlbG93ICh6b29tIGluIG9uIHRoZSB0b3ApIG9uZSBjYW4gc2VlIHRoYXQgdGhlIGNoYWluIGlzIHN0aWxsIG5vdCBidXJuZWQtaW4sIGFzIHRoZSBwb3N0ZXJpb3IgaXMgbm90IGZsYXQuDQpUaGlzIGlzIGluIGZhY3QgcmVmbGVjdGVkIGluIGEgc2xvdyBkcmlmdCBvZiBgbHdpZHRoc2Agd2hpY2ggYXJlIHN0aWxsIHF1aXRlIHdyb25nIGZvciB0aGUgd2lkZSBwZWFrcy4gVGhpcyBpcyBub3QgdHJpdmlhbCB0byBzZWUgdXNpbmcgdHJhY2VwbG90cyBhbmQgb3RoZXIgZGlhZ25vc3RpY3MuDQoqKlpvb20gdG8gbWFnbmlmeSoqDQpgYGB7cn0NCnBsdC5wb3N0ZXJpb3IocmVzJHBvc3RfdmFscywgc3RlcHMgPSAxMDAwKQ0KYGBgDQoNCg0KIyMjIGBwbHQucm9sbGluZ19hY2NlcHRpb25zYA0KV2UgbWlnaHQgYWxzbyBiZSBpbnRlcmVzdGVkIGluIHRoZSBhY2NlcHRhbmNlIHJhdGUuIEluIHRoZSBjYXNlIHRoYXQgb3VyIGNoYWluIGhhc24ndCBmdWxseSBidXJuZWQgaW4geWV0IGEgcm9sbGluZyBtZWFuIC8gd2luZG93ZWQgdmVyc2lvbiBvZiB0aGUgYWNjZXB0YW5jZSByYXRlIGlzIHVzZWZ1bC4NCg0KDQpgYGB7cn0NCnBsdC5yb2xsaW5nX2FjY2VwdGlvbnMocmVzJGFjY2VwdGlvbnMsIHdpbmRvd3NpemUgPSA1MDAsIHN0ZXBzID0gMTAwMCkNCnJlc19NSE1DMSA8LSByZXMgIyBzYXZlIGZvciBhcHBlbmRpeA0KYGBgDQoNCiMjIFByb2JsZW1zIGZvciBNSCAtIFBlcmZlY3QgU3RhcnQ6DQoNCg0KRmluZGluZyBhIGdvb2QgcHJvcG9zYWwgZGlzdHJpYnV0aW9uLCBzdWNoIGFzIHRoZSBvbmUgYWJvdmUsIHRha2VzIGEgbG90IG9mIGh5cGVycGFyYW1ldGVyIHR1bmluZy4NCg0KIyMjIERpZmZlcmVudCBQZWFrIFdpZHRocw0KDQpUaGUgdGhpbm5lciBhbmQgaGlnaGVyIGEgcGVhayBpcyB0aGUgcHJlY2lzZXIgaXRzIGBwb3NgLCBgbHdpZHRoYCBhbmQgYGd3aWR0aGAgYmVjb21lcy4gVGh1cyB0aGlubmVyIHBlYWtzIG5lZWQgZmluZXIgcHJvcG9zYWxzLg0KVGhlIHRyYWNlcGxvdHMgKHNob3duIGFib3ZlKSBmb3IgdGhlIHRoaW5uZXN0IHBlYWsgYXJlIHZlcnkgZ29vZCAoYHBvc1sxXWAsIGBsd2lkdGhbMV1gLCBgZ3dpZHRoWzFdYCkgYnV0IHRoZSBwcm9wb3NhbCBpcyB0b28gZmluZSBmb3IgdGhlIG90aGVyIHBlYWtzIChhbnkgb3RoZXIgdHJhY2UpLiBIZW5jZSB0aGVyZSBpcyBkcmlmdCBpbiB0aGVpciB0cmFjZXMgKGkuZS4gYHBvc1syXWApLiBEdWUgdG8gdGhlIHRoaW4gcGVhayBhIGNvYXJzZXIgcHJvcG9zYWwgeWllbGRzIGEgbWFzc2l2ZSBkcm9wIGluIHRoZSBhY2NlcHRhbmNlIHJhdGUgYW5kIGlzbid0IGEgdmlhYmxlIHNvbHV0aW9uIHRvIHRoZSBwcm9ibGVtIGVpdGhlci4gQ29uc2VxdWVudGx5IHRoZXJlIGlzIG5vIHdheSB0byBmaW5kIGh5cGVycGFyYW1ldGVycyB0aGF0IGhhdmUgaGlnaCBhY2NlcHRhbmNlIGFuZCBhIGxvdyBkcmlmdCBpbiB0aGUgdHJhY2VzLg0KDQpPbmUgc2ltcGxlIGZpeCB0aGF0IGdldHMgYXJvdW5kIHRoaXMgcHJvYmxlbSBpcyB0byBoYXZlIGFuIGFzeW1tZXRyaWMgcHJvcG9zYWwgZm9yIHdoaWNoIHRoZSBzdGFuZGFyZCBkZXZpYXRpb24gKGBzZGApIG9mIHRoZSBgcG9zYCwgYGd3aWR0aGAgYW5kIGBsd2lkdGhgIHByb3Bvc2FsIGlzIGdpdmVuIGJ5IGBzZCA8LSB1bml0X3NkICogc3FydChnd2lkdGheMiArIGx3aWR0aF4yKS91bml0X3dpZHRoYCAod2l0aCBoeXBlcnBhcmFtZXRlcnMgYHVuaXRfc2RgIGFuZCBgdW5pdF93aWR0aGApIChpbXBsZW1lbnRhdGlvbiBpbiA8YSBvbmNsaWNrPSdqdW1wX2hlYWRlcigiQXBwZW5kaXggRCIpOyc+QXBwZW5kaXggRDwvYT4pLiBUaHVzIHdpZGVyIHBlYWtzIGhhdmUgYSBiaWdnZXIgYHNkYCBhbmQgdGhpbm5lciBwZWFrcyBhIHNtYWxsZXIgYHNkYC4gVGhpcyBwcm9wb3NhbCBhY3R1YWxseSBpcyByYXRoZXIgaW1wb3J0YW50IGFuZCBmaXhlcyBpc3N1ZXMgdGhhdCBvY2N1ciBpbiB0aGUgYnVybi1pbiBvZiB0aGUgY2hhaW4gYW5kIGlzIHRoZXJlZm9yZSBhbHNvIHVzZWQgaW4gdGhlIGZvbGxvd2luZyBzZWN0aW9uICdCdXJuLWluIFByb2JsZW1zJy4NCmBgYHtyfQ0KcHJvcG9zYWwyIDwtIFByb3Bvc2FsKA0KICBpc19zeW1tZXRyaWMgPSBGQUxTRSwNCiAgYW1wID0gcHJvcC5Ob3JtKDAuMDAwNSksICNzZA0KICBsd2lkdGggPSBwcm9wLk5vcm1Qcm9wVG9XaWR0aCgwLjAwMywgMS40KSwNCiAgZ3dpZHRoID0gcHJvcC5Ob3JtUHJvcFRvV2lkdGgoMC4wMDMsIDEuNCksICMgdW5pdF9zZCwgdW5pdF93aWR0aC4gU3VjaCB0aGF0IGZvciBhIHBlYWsgb2Ygd2lkdGggPSB1bml0X3dpZHRoIHRoZSBzZCBpcyB1bml0X3NkLiBTY2FsZXMgbGluZWFybHkgZm9yIGRpZmZlcmVudCBwZWFrIHdpZHRocy4NCiAgcG9zID0gcHJvcC5Ob3JtUHJvcFRvV2lkdGgoMC4wMDA1LCAxLjQpLA0KICBhID0gcHJvcC5Ob3JtKDAuMDAwMDAwMDUpLA0KICBiID0gcHJvcC5Ob3JtKDAuMDAwMDIpICNiID0gcHJvcC5VbmlmKDAuMDIpDQopDQoNCnJlcyA8LSBjaGFpbnMubWhtYyh4LCB5LCBnb29kX3N0YXJ0LCBwb3N0ZXJpb3IsIHByb3Bvc2FsMiwgaXRlciA9IDQwMDAwLCBwcmludF9iYXIgPSBGQUxTRSkNCmBgYA0KDQpUaGUgcmVzdWx0aW5nIGNoYWluIGlzIGZhciBiZXR0ZXIsIHdoaWNoIGJlY29tZXMgb2J2aW91cyB3aGVuIGxvb2tpbmcgYXQgdGhlIHRhY2VwbG90IGZvciBgbHdpZHRoYCBhbmQgY29tcGFyaW5nIGl0IHRvIHRoZSB0cmFjZXBsb3QgZm9yIGBsd2lkdGhgIHVzaW5nIGFuIHN5bW1ldHJpYyBwcm9wb3NhbCAoPGEgb25jbGljaz0nanVtcF9tYXJrZWQoImx3aWR0aGZpcnN0Y2hhaW4iKTsnPmhlcmU8L2E+KS4NCkFsbCBvdGhlciBkaWFnbm9zdGljcyBpbiB0aGUgPGEgb25jbGljaz0nanVtcF9oZWFkZXIoIkFwcGVuZGl4IEIiKTsnPiBBcHBlbmRpeCBCPC9hPg0KYGBge3J9DQojIENvbXBhcmUgdGhpcyB0byBwcmV2aW91cyAnVHJhY2UgcGxvdCBvZiBsd2lkdGgnDQpwbHQudHJhY2VwbG90KHJlcyRzYW1wbGVzLCAibHdpZHRoIiwgc3RlcHM9MjAwMCkNCnJlc19NSE1DMiA8LSByZXMgI3NhdmUgZm9yIGFwcGVuZGl4DQpgYGANCg0KIyMjIFNhZGRsZSBQb2ludHMgLyBNb2Rlcw0KQmVsb3cgaXMgYSBjaGFpbiBzaG93biB0aGF0IGlzIHN0dWNrIGluIGEgbG9jYWwgbWF4IC8gc2FkZGxlIHBvaW50Lg0KV2h5IGlzIGl0IHN0dWNrPyBUaGUgc3RhcnRpbmcgcG9pbnQgaXMgYWxyZWFkeSBidXJuZWQtaW4gYXMgaXQgaXMgYSBzYW1wbGUgZnJvbSB0aGUgYnVybmVkLWluIGNoYWluIGZyb20gdGhlIGZvbGxvd2luZyBzZWN0aW9uICdCdXJuLWluIFByb2JsZW1zJy4gT2J0YWluaW5nIHRoaXMgYnVybmVkLWluIGNoYWluIHRha2VzIHJvdWdobHkgYSBtaWxsaW9uIGl0ZXJhdGlvbnMgYW5kIDUgZGlmZmVyZW50IHByb3Bvc2Fscy4NClRoZSB0cmFjZXBsb3RzIGFuZCBwb3N0ZXJpb3IgcGxvdHMgYXJlIGZsYXQgd2hpY2ggaGVhdmlseSBzdWdnZXN0cyB0aGF0IHRoZSBjaGFpbiBpcyBpbmRlZWQgYnVybmVkLWluLCB0aGlzIHRvbyBjb25maXJtcyB0aGUgYXNzdW1wdGlvbiB0aGF0IHRoZSBzdGFydGluZyBwb2ludCBpcyBhbHJlYWR5IGJ1cm5lZCBpbi4NCg0KSG93ZXZlciBpZiB0aGlzIGlzIHRoZSBjYXNlLCB0aGUgY2hhaW4gaXMgc3R1Y2sgaW4gYSBxdWl0ZSBiYWQgbG9jYWwgbWF4IC8gc2FkZGxlIHBvaW50LCBvbmx5IGZpdHRpbmcgMyBwZWFrcy4gQXMgc2hvd24gYmVsb3csIHRoZSBjaGFpbiBjYW4ndCBsZWF2ZSB0aGUgc2FkZGxlIHBvaW50IGV2ZW4gYWZ0ZXIgMzAwayBpdGVyYXRpb25zLiBUaGVyZSBpcyBubyB3YXkgdG8gZml4IHRoaXMgdHJpdmlhbGx5IHdpdGggYSBsb2NhbCBtZXRob2QgKGFueSBsb2NhbCBwcm9wb3NhbCkgYXMgdGhpcyBpcyBhbiBpbm5hdGUgcHJvcGVydHkgb2YgdGhlIHBvc3Rlcmlvci4NCg0KTW9yZSBwcmVjaXNlbHksIHRoZSByZWFzb24gZm9yIHRoaXMgaXNzdWUgaGVyZSBpcyB0aGF0IHRoZXJlIGlzIGEgbWlzcGxhY2VkIHBlYWsgKGBwb3NbM11gKSBpbnN0ZWFkIG9mIGl0IGJlaW5nIGF0IHRoZSBsb2NhdGlvbiBvZiB0aGUgZmlyc3QgdHdpbiBwZWFrcyBpdCBpcyBsb2NhdGVkIGluIGEgZmxhdCByZWdpb24gb2YgdGhlIHNpZ25hbC4NCg0KVGhpcyBtaWdodCBiZSBhIHNhZGRsZSBwb2ludCBhbmQgcmVzb2x2ZSBpdHNlbGYgYWZ0ZXIgYSBmZXcgbWlsbGlvbiBpdGVyYXRpb25zLiBIb3dldmVyIGl0IGlzIGluLWZlYXNpYmxlIHRvIGNhbGN1bGF0ZSBzbyBtYW55IGl0ZXJhdGlvbnMuDQpgYGB7cn0NCnN0YXJ0IDwtIGxpc3QoDQogIHBvcyA9IGMoMTM5LjQ2MzYsIDQ4Ni40MTA4LCA4NzMuMzgxOSwgNDE5LjE2ODQpLA0KICBnd2lkdGggPSBjKDI1LjUzNTE3ODgsIDAuOTgzMDMzMiwgMjY2Ljg4OTk2NTUsIDIuNzcyNzc4MSksDQogIGx3aWR0aCA9IGMoMC4wNTk5ODE0MywgMS4wODQ2Njg4MCwgMjUuMjcyNjY0NzEsIDkuODY5NTY0MDApLA0KICBhbXAgPSBjKDIuMDg5NTk0OSwgMi44MDYyNzcwLCAwLjgzNTM2NjAsIDAuNTk4NTk0OSksDQogIGEgPSAtMi43NDg2NDZlLTA2LA0KICBiID0gMjAuMDEyNjQNCikNCg0KIyBub24gc3ltbWV0cmljIHByb3Bvc2FsIHRvIGFsbG93IGZvciBiZXR0ZXIgcG9zaXRpb24gZGlmZnVzaW9uDQpwcm9wb3NhbDIgPC0gUHJvcG9zYWwoDQogIGlzX3N5bW1ldHJpYyA9IEZBTFNFLA0KICBhbXAgPSBwcm9wLk5vcm0oMC4wMDA1KSwNCiAgbHdpZHRoID0gcHJvcC5Ob3JtUHJvcFRvV2lkdGgoMC4wMDMsIDEuNCksDQogIGd3aWR0aCA9IHByb3AuTm9ybVByb3BUb1dpZHRoKDAuMDAzLCAxLjQpLCAjIHVuaXRfc2QsIHVuaXRfd2lkdGguIFN1Y2ggdGhhdCBmb3IgYSBwZWFrIG9mIHdpZHRoID0gdW5pdF93aWR0aCB0aGUgc2QgaXMgdW5pdF9zZC4gU2NhbGVzIGxpbmVhcmx5IGZvciBkaWZmZXJlbnQgcGVhayB3aWR0aHMuDQogIHBvcyA9IHByb3AuTm9ybVByb3BUb1dpZHRoKDAuMDAwNSwgMS40KSwNCiAgYSA9IHByb3AuTm9ybSgwLjAwMDAwMDA1KSwNCiAgYiA9IHByb3AuTm9ybSgwLjAwMDAyKQ0KKQ0KDQpyZXMgPC0gY2hhaW5zLm1obWMoeCwgeSwgc3RhcnQsIHBvc3RlcmlvciwgcHJvcG9zYWwyLCBpdGVyID0gMzAwMDAwLCBwcmludF9iYXIgPSBGQUxTRSkNCmBgYA0KDQpJbiB0aGUgZm9sbG93aW5nIHBsb3RzIHdlIGNhbiBzZWUgaG93IGl0IGZhaWxzIHRvIGZpdCB0aGUgdHdpbiBwZWFrcywgYW5kIGluIHRoZSB0cmFjZXBsb3Qgb2YgYHBvc2AgdGhlIG1pc3NpbmcvbWlzcGxhY2VkIHBlYWsgY2FuIGJlIGZvdW5kIGF0IGBwb3MgYXBwcm94LiA5MDBgLg0KQWxsIG90aGVyIGRpYWdub3N0aWNzIGFyZSBpbiB0aGUgPGEgb25jbGljaz0nanVtcF9oZWFkZXIoIkFwcGVuZGl4IEMiKTsnPiBBcHBlbmRpeCBDPC9hPg0KYGBge3J9DQpwbHQuZml0LmNoYWluLmludGVyYWN0aXZlKHgsIHksIHJlcyRzYW1wbGVzLCBzaWduYWxfbW9kZWwsIHN0ZXBfd2lkdGggPSA2MDAwMCkNCnBsdC50cmFjZXBsb3QocmVzJHNhbXBsZXMsICJwb3MiLCBzdGVwcyA9IDIwMDApDQpyZXNfTUhNQzMgPC0gcmVzDQpgYGANCg0KIyBNSCAtIFJhbmRvbSBTdGFydA0KIyMgQnVybi1pbiBQcm9ibGVtcw0KTWFqb3IgYnVybi1pbiBwcm9ibGVtcyB3aGVuIHN0YXJ0aW5nIGZyb20gYW4gcHJvYmxlbSBhZ25vc3RpYyBzdGFydGluZyBwb2ludCB0aGF0IGlzIHJhbmRvbSAoYHByb3Audm9pZ3Qucm5kX3N0YXJ0YCkuDQpBIGNvYXJzZSBwcm9wb3NhbCBpcyBuZWVkZWQgZm9yIGEgZmFzdCBidXJuLWluLiBUaGlzIGhvd2V2ZXIgY2F1c2VzIGEgdGlueSBhY2NlcHRhbmNlIHJhdGUgb25jZSBidXJuZWQgaW4uIFRodXMgZGlmZmVyZW50IHByb3Bvc2FscyBmb3IgdGhlIGJ1cm4taW4gYW5kIGJ1cm5lZC1pbiBzdGF0ZSBhcmUgdXNlZC4NCklmIHdlIHdlcmUgdG8gdXNlIHRoZSBmaW5lIHByb3Bvc2FsIGZyb20gdGhlIHByZXZpb3VzIHNlY3Rpb25zIHRoZSBidXJuLWluIGlzIDEwKyBtaWxsaW9uIGl0ZXJhdGlvbnMgbG9uZy4NCkluc3RlYWQgd2Ugc3RhcnQgd2l0aCBhIHZlcnkgY29hcnNlIHByb3Bvc2FsIGFuZCBtYWtlIGl0IGV2ZXJ5IGZldyAxMDAwMCBpdGVyYXRpb25zIGZpbmVyLiBDbGVhcmx5IHRoaXMgaXMgcXVpdGUgbWVzc3kgYW5kIHZlcnkgdGltZSBpbnRlbnNpdmUgYXMgb25lIGhhcyB0byB0dW5lLCBpbiB0aGlzIGNhc2UsIDUgcHJvcG9zYWxzLg0KDQpJbiB0aGUgZm9sbG93aW5nIHZlcnkgbG9uZyBidXJuLWluIGNvZGUsIGEgZmV3IGludGVyZXN0aW5nIHRoaW5ncyBvY2N1ci4gRmlyc3QgYSBwZWFrIChwZWFrIDMpIG1vdmVzIGluIGEgdGVycmlibGUgcG9zaXRpb24gZHVlIHRvIGEgb2RkIHNhZGRsZSBwb2ludCBpbiB0aGUgcG9zdGVyaW9yLCB0aGlzIGNhbiBiZSBzZWVuIHZlcnkgb2J2aW91c2x5IGluIHRoZSBzZWNvbmQgaW50ZXJhY3RpdmUgZml0IHBsb3RzLiBTZWNvbmRseSwgaW4gdGhlIGVhcmx5IHN0YWdlcyBvZiB0aGUgYnVybi1pbiB0d28gcGVha3MgZml0IHRoZSBsYXJnZSBwZWFrIHdpdGggZXhhY3RseSB0aGUgc2FtZSBwb3NpdGlvbi4gSXQgdGFrZXMgNDAwayBpdGVyYXRpb25zIHdpdGggdGhlIHNwZWNpYWwgYXN5bW1ldHJpYyBwcm9wb3NhbCB0byBwdWxsIHRoZW0gYXBhcnQgYW5kIHRvIGdldCBvbmUgb2YgdGhlbSB0byBmaXQgdGhlIHNtYWxsIHBlYWsgbmV4dCB0byB0aGUgYmlnIG9uZS4NCg0KYGBge3J9DQpzdGFydCA8LSBwcm9wLnZvaWd0LnJuZF9zdGFydCgzNDMsIGZpdF9rcCkNCg0KIyBmaXJzdCBwcm9wb3NhbCwgYnVybnMtaW4gYi4NCmJfYnVybmluIDwtIFByb3Bvc2FsKA0KICBpc19zeW1tZXRyaWMgPSBUUlVFLA0KICBhbXAgPSBwcm9wLk5vcm0oMC4wMDA1KSwNCiAgbHdpZHRoID0gcHJvcC5Ob3JtKDAuMDAzKSwNCiAgZ3dpZHRoID0gcHJvcC5Ob3JtKDAuMDAzKSwNCiAgcG9zID0gcHJvcC5Ob3JtKDAuMDA1KSwNCiAgYSA9IHByb3AuTm9ybSgwLjAwMDAwMDA1KSwNCiAgYiA9IHByb3AuTm9ybSgwLjIpDQopDQpyZXMgPC0gY2hhaW5zLm1obWMoeCwgeSwgc3RhcnQsIHBvc3RlcmlvciwgYl9idXJuaW4sIGl0ZXIgPSAxMDAwLCBwcmludF9iYXIgPSBGQUxTRSkNCnBsdC5maXQuY2hhaW4uaW50ZXJhY3RpdmUoeCwgeSwgcmVzJHNhbXBsZXMsIHNpZ25hbF9tb2RlbCwgc3RlcF93aWR0aCA9IDIwMCkNCnN0YXJ0IDwtIHJlcyRzYW1wbGVzW1tsZW5ndGgocmVzJHNhbXBsZXMpXV0NCg0KDQojIHNlY29uZCBwcm9wb3NhbCwgYnVybnMtaW4gcG9zLiBpbnRlcmVzdGluZ2x5IGFzIHBsdC5maXQuY2hhaW4uaW50ZXJhY3RpdmUgc2hvd3Mgb25lIHBlYWsgbW92ZXMgaW50byB0aGUgdGhlIHdyb25nIGRpcmVjdGlvbi4NCiMgVGhpcyBpcyBkdWUgdG8gdGhlIG5vbi10cml2aWFsIGxvY2FsLW1heGltYSAvIHNhZGRsZSBwb2ludCBzdHJ1Y3R1cmUgb2YgdGhlIHBvc3Rlcmlvci4NCnBvc19idXJuaW4gPC0gUHJvcG9zYWwoDQogIGlzX3N5bW1ldHJpYyA9IFRSVUUsDQogIGFtcCA9IHByb3AuTm9ybSgwLjAwMDUpLA0KICBsd2lkdGggPSBwcm9wLk5vcm0oMC4wMDMpLA0KICBnd2lkdGggPSBwcm9wLk5vcm0oMC4wMDMpLA0KICBwb3MgPSBwcm9wLk5vcm0oMC41KSwNCiAgYSA9IHByb3AuTm9ybSgwLjAwMDAwMDA1KSwNCiAgYiA9IHByb3AuTm9ybSgwLjAwMDAyKQ0KKQ0KcmVzIDwtIGNoYWlucy5taG1jKHgsIHksIHN0YXJ0LCBwb3N0ZXJpb3IsIHBvc19idXJuaW4sIGl0ZXIgPSAyMDAwMCwgcHJpbnRfYmFyID0gRkFMU0UpDQpwbHQuZml0LmNoYWluLmludGVyYWN0aXZlKHgsIHksIHJlcyRzYW1wbGVzLCBzaWduYWxfbW9kZWwsIHN0ZXBfd2lkdGggPSAxMDAwKQ0Kc3RhcnQgPC0gcmVzJHNhbXBsZXNbW2xlbmd0aChyZXMkc2FtcGxlcyldXQ0KDQojIGJ1cm4gaW4gZm9yIHdpZHRocw0Kd2lkdGhfYnVybmluIDwtIFByb3Bvc2FsKA0KICBpc19zeW1tZXRyaWMgPSBUUlVFLA0KICBhbXAgPSBwcm9wLk5vcm0oMC4wMDA1KSwNCiAgbHdpZHRoID0gcHJvcC5Ob3JtKDAuMDA5KSwNCiAgZ3dpZHRoID0gcHJvcC5Ob3JtKDAuMDA5KSwNCiAgcG9zID0gcHJvcC5Ob3JtKDAuMDQpLA0KICBhID0gcHJvcC5Ob3JtKDAuMDAwMDAwMDUpLA0KICBiID0gcHJvcC5Ob3JtKDAuMDAwMDIpDQopDQpyZXMgPC0gY2hhaW5zLm1obWMoeCwgeSwgc3RhcnQsIHBvc3Rlcmlvciwgd2lkdGhfYnVybmluLCBpdGVyID0gMjAwMDAsIHByaW50X2JhciA9IEZBTFNFKQ0KcGx0LmZpdC5jaGFpbi5pbnRlcmFjdGl2ZSh4LCB5LCByZXMkc2FtcGxlcywgc2lnbmFsX21vZGVsLCBzdGVwX3dpZHRoID0gMTAwMCkNCnN0YXJ0IDwtIHJlcyRzYW1wbGVzW1tsZW5ndGgocmVzJHNhbXBsZXMpXV0NCg0KIyBuZXh0IGJ1cm4gaW4NCm5leHRfYnVybmluIDwtIFByb3Bvc2FsKA0KICBpc19zeW1tZXRyaWMgPSBUUlVFLA0KICBhbXAgPSBwcm9wLk5vcm0oMC4wMDA1KSwgI3NkDQogIGx3aWR0aCA9IHByb3AuTm9ybSgwLjAwOSksDQogIGd3aWR0aCA9IHByb3AuTm9ybSgwLjAwOSksICMgY2FuIGFsc28gdXNlIHByb3AuVW5pZihyYWRpdXMpIGZvciB1bmlmb3JtIHByb3Bvc2FsDQogIHBvcyA9IHByb3AuTm9ybSgwLjA0KSwNCiAgYSA9IHByb3AuTm9ybSgwLjAwMDAwMDA1KSwNCiAgYiA9IHByb3AuTm9ybSgwLjAwMDAyKSAjYiA9IHByb3AuVW5pZigwLjAyKQ0KKQ0KcmVzIDwtIGNoYWlucy5taG1jKHgsIHksIHN0YXJ0LCBwb3N0ZXJpb3IsIG5leHRfYnVybmluLCBpdGVyID0gMTAwMDAwLCBwcmludF9iYXIgPSBGQUxTRSkNCnBsdC5maXQuY2hhaW4uaW50ZXJhY3RpdmUoeCwgeSwgcmVzJHNhbXBsZXMsIHNpZ25hbF9tb2RlbCwgc3RlcF93aWR0aCA9IDUwMDApDQpzdGFydCA8LSByZXMkc2FtcGxlc1tbbGVuZ3RoKHJlcyRzYW1wbGVzKV1dDQoNCmFzeW1fYnVybmluIDwtIFByb3Bvc2FsKA0KICBpc19zeW1tZXRyaWMgPSBGQUxTRSwNCiAgYW1wID0gcHJvcC5Ob3JtKDAuMDAwNSksDQogIGx3aWR0aCA9IHByb3AuTm9ybVByb3BUb1dpZHRoKDAuMDAzLCAxLjQpLA0KICBnd2lkdGggPSBwcm9wLk5vcm1Qcm9wVG9XaWR0aCgwLjAwMywgMS40KSwgIyB1bml0X3NkLCB1bml0X3dpZHRoLiBTdWNoIHRoYXQgZm9yIGEgcGVhayBvZiB3aWR0aCA9IHVuaXRfd2lkdGggdGhlIHNkIGlzIHVuaXRfc2QuIFNjYWxlcyBsaW5lYXJseSBmb3IgZGlmZmVyZW50IHBlYWsgd2lkdGhzLg0KICBwb3MgPSBwcm9wLk5vcm1Qcm9wVG9XaWR0aCgwLjAwMDUsIDEuNCksDQogIGEgPSBwcm9wLk5vcm0oMC4wMDAwMDAwNSksDQogIGIgPSBwcm9wLk5vcm0oMC4wMDAwMikNCikNCiMgYWxtb3N0IGZpbmFsIGJ1cm4gaW4gKHBsZWFzZSkNCnJlcyA8LSBjaGFpbnMubWhtYyh4LCB5LCBzdGFydCwgcG9zdGVyaW9yLCBhc3ltX2J1cm5pbiwgaXRlciA9IDE1MDAwMCwgcHJpbnRfYmFyID0gRkFMU0UpDQpwbHQuZml0LmNoYWluLmludGVyYWN0aXZlKHgsIHksIHJlcyRzYW1wbGVzLCBzaWduYWxfbW9kZWwsIHN0ZXBfd2lkdGggPSA1MDAwKQ0Kc3RhcnQgPC0gcmVzJHNhbXBsZXNbW2xlbmd0aChyZXMkc2FtcGxlcyldXQ0KDQojIGNhbid0IGJlIGJ1cm5lZCBpbiB5ZXQgYXMgcG9zdGVyaW9yIGlzbid0IGZsYXQNCnBsdC5wb3N0ZXJpb3IocmVzJHBvc3RfdmFscykNCg0KIyBub24gc3ltbWV0cmljIHByb3Bvc2FsIHRvIGFsbG93IGZvciBiZXR0ZXIgcG9zaXRpb24gZGlmZnVzaW9uDQpwcm9wb3NhbDIgPC0gUHJvcG9zYWwoDQogIGlzX3N5bW1ldHJpYyA9IEZBTFNFLA0KICBhbXAgPSBwcm9wLk5vcm0oMC4wMDA1KSwNCiAgbHdpZHRoID0gcHJvcC5Ob3JtUHJvcFRvV2lkdGgoMC4wMDMsIDEuNCksDQogIGd3aWR0aCA9IHByb3AuTm9ybVByb3BUb1dpZHRoKDAuMDAzLCAxLjQpLCAjIHVuaXRfc2QsIHVuaXRfd2lkdGguIFN1Y2ggdGhhdCBmb3IgYSBwZWFrIG9mIHdpZHRoID0gdW5pdF93aWR0aCB0aGUgc2QgaXMgdW5pdF9zZC4gU2NhbGVzIGxpbmVhcmx5IGZvciBkaWZmZXJlbnQgcGVhayB3aWR0aHMuDQogIHBvcyA9IHByb3AuTm9ybVByb3BUb1dpZHRoKDAuMDAwNSwgMS40KSwNCiAgYSA9IHByb3AuTm9ybSgwLjAwMDAwMDA1KSwNCiAgYiA9IHByb3AuTm9ybSgwLjAwMDAyKQ0KKQ0KDQpzdGFydCA8LSByZXMkc2FtcGxlc1tbbGVuZ3RoKHJlcyRzYW1wbGVzKV1dDQpyZXMgPC0gY2hhaW5zLm1obWMoeCwgeSwgc3RhcnQsIHBvc3RlcmlvciwgcHJvcG9zYWwyLCBpdGVyID0gMzAwMDAwLCBwcmludF9iYXIgPSBGQUxTRSkNCg0KcGx0LmZpdC5jaGFpbi5pbnRlcmFjdGl2ZSh4LCB5LCByZXMkc2FtcGxlcywgc2lnbmFsX21vZGVsLCBzdGVwX3dpZHRoID0gNjAwMDApDQpwbHQudHJhY2VwbG90KHJlcyRzYW1wbGVzLCAicG9zIiwgc3RlcHMgPSAyMDAwKQ0KcGx0LnRyYWNlcGxvdChyZXMkc2FtcGxlcywgImFtcCIsIHN0ZXBzID0gMjAwMCkNCnBsdC50cmFjZXBsb3QocmVzJHNhbXBsZXMsICJnd2lkdGgiLCBzdGVwcyA9IDIwMDApDQpwbHQudHJhY2VwbG90KHJlcyRzYW1wbGVzLCAibHdpZHRoIiwgc3RlcHMgPSAyMDAwKQ0KcGx0LnBvc3RlcmlvcihyZXMkcG9zdF92YWxzLCBzdGVwcyA9IDEwMDApDQpwbHQucm9sbGluZ19hY2NlcHRpb25zKHJlcyRhY2NlcHRpb25zLCB3aW5kb3dzaXplID0gMTAwMCwgc3RlcHMgPSAxMDAwKQ0KYGBgDQojIEFwcGVuZGl4DQojIyBBcHBlbmRpeCBBDQoNClBsb3RzIGZvciB0aGUgZmlyc3QgQ2hhaW4uDQpgYGB7cn0NCnBsdC50cmFjZXBsb3QocmVzX01ITUMxJHNhbXBsZXMsICJnd2lkdGgiLCBzdGVwcz0yMDAwKQ0KcGx0LnRyYWNlcGxvdChyZXNfTUhNQzEkc2FtcGxlcywgImFtcCIsIHN0ZXBzPTIwMDApDQpwbHQudHJhY2VwbG90KHJlc19NSE1DMSRzYW1wbGVzLCAiYSIsIHN0ZXBzPTIwMDApDQpwbHQudHJhY2VwbG90KHJlc19NSE1DMSRzYW1wbGVzLCAiYiIsIHN0ZXBzPTIwMDApDQpgYGANCg0KIyMgQXBwZW5kaXggQg0KDQpQbG90cyBmb3IgdGhlIHNlY29uZCBDaGFpbi4NCmBgYHtyfQ0KcGx0LmZpdC5jaGFpbi5pbnRlcmFjdGl2ZSh4LCB5LCByZXNfTUhNQzIkc2FtcGxlcywgc2lnbmFsX21vZGVsLCBzdGVwX3dpZHRoID0gNTAwMCkNCnBsdC50cmFjZXBsb3QocmVzX01ITUMyJHNhbXBsZXMsICJwb3MiLCBzdGVwcz0yMDAwKQ0KcGx0LnRyYWNlcGxvdChyZXNfTUhNQzIkc2FtcGxlcywgImd3aWR0aCIsIHN0ZXBzPTIwMDApDQpwbHQudHJhY2VwbG90KHJlc19NSE1DMiRzYW1wbGVzLCAiYW1wIiwgc3RlcHM9MjAwMCkNCnBsdC50cmFjZXBsb3QocmVzX01ITUMyJHNhbXBsZXMsICJhIiwgc3RlcHM9MjAwMCkNCnBsdC50cmFjZXBsb3QocmVzX01ITUMyJHNhbXBsZXMsICJiIiwgc3RlcHM9MjAwMCkNCnBsdC5wb3N0ZXJpb3IocmVzX01ITUMyJHBvc3RfdmFscywgc3RlcHMgPSAxMDAwKQ0KcGx0LnJvbGxpbmdfYWNjZXB0aW9ucyhyZXNfTUhNQzIkYWNjZXB0aW9ucywgd2luZG93c2l6ZSA9IDEwMDAsIHN0ZXBzID0gMTAwMCkNCmBgYA0KDQojIyBBcHBlbmRpeCBDDQoNCmBgYHtyfQ0KcGx0LmZpdC5jaGFpbi5pbnRlcmFjdGl2ZSh4LCB5LCByZXNfTUhNQzMkc2FtcGxlcywgc2lnbmFsX21vZGVsLCBzdGVwX3dpZHRoID0gNTAwMCkNCnBsdC50cmFjZXBsb3QocmVzX01ITUMzJHNhbXBsZXMsICJwb3MiLCBzdGVwcz0yMDAwKQ0KcGx0LnRyYWNlcGxvdChyZXNfTUhNQzMkc2FtcGxlcywgImd3aWR0aCIsIHN0ZXBzPTIwMDApDQpwbHQudHJhY2VwbG90KHJlc19NSE1DMyRzYW1wbGVzLCAiYW1wIiwgc3RlcHM9MjAwMCkNCnBsdC50cmFjZXBsb3QocmVzX01ITUMzJHNhbXBsZXMsICJhIiwgc3RlcHM9MjAwMCkNCnBsdC50cmFjZXBsb3QocmVzX01ITUMzJHNhbXBsZXMsICJiIiwgc3RlcHM9MjAwMCkNCnBsdC5wb3N0ZXJpb3IocmVzX01ITUMzJHBvc3RfdmFscywgc3RlcHMgPSAxMDAwKQ0KcGx0LnJvbGxpbmdfYWNjZXB0aW9ucyhyZXNfTUhNQzMkYWNjZXB0aW9ucywgd2luZG93c2l6ZSA9IDEwMDAsIHN0ZXBzID0gMTAwMCkNCmBgYA0KDQojIyBBcHBlbmRpeCBEDQpgYGB7ciBldmFsPUZBTFNFfQ0KIyBwcm9wLk5vcm1Qcm9wVG9XaWR0aCBlc3NlbnRpYWxseSBkb2VzLCB3aGVyZSB4IGNvdWxkIGJlIHBvcywgbHdpZHRoIG9yIGd3aWR0aDoNCndpZHRoIDwtIHNxcnQocGFyYW1zJGd3aWR0aF4yICsgcGFyYW1zJGx3aWR0aF4yKQ0Kc2QgPC0gd2lkdGggLyBzZWxmJHVuaXRfd2lkdGggKiBzZWxmJHVuaXRfc2QNCnJub3JtKGxlbmd0aCh4KSwgbWVhbiA9IHgsIHNkKQ0KDQpgYGANCklkZWFzOg0KLSBDaGFuZ2Ugb2YgdmFyaWFibGVzOyBpbnN0ZWFkIG9mIGx3aWR0aCwgZ3dpZHRoIHVzZSBGV0hNIGFuZCBldGEu