RJMCMC-burnin Gibbs Sampler (fits exactly) >> Gibbs Sampler

As Pdf

1 Data

library(spectralmc)

true_kp <- 9
fit_kp <- 9
noise_sigma <- 0.001
signal_model <- voigt.model

data <- data.synthetic.stormyclouds(seed = 53252, kp = true_kp, noise = 0.001)
x <- data$x
y <- data$y

2 Gibbs Sampler

Uses a collapsed (marginalizes over a, b, amp numerically) gibbs sampler which approximates the conditionals with slice sampling. Also re-parameterizes density with (sqrt(gwidth^2+lwidth^2), gwidth-lwidth) instead of (gwidth, lwitdh) if min(gwidth/lwidth, lwidth/gwidth) > 0.05 (otherwise transformation becomes unstable). Uses a Likelihood-ratio test to stop slice sampling early. Slice sampling rerun a few times (3-5) to generate draws from which one is sampled according to their posterior density (prevents slice sampling failure).

  • Takes 1-4s per peak and iteration
    • 22h runtime for this notebook
    • Thus only usable with a remote cluster (I wont use anymore of my google.colab quota for this)
  • short burn-in
  • start_point is essentially irrelevant (modulo the label switching problem, gelman-rubin will be terrible due to that)
  • small auto-correlation (looks like it in traces at least)
  • fits ok
  • proposal free
  • non-informative priors
    • prior_a = Unif(1e-2, 1e2)
    • prior_b = Unif(-Inf, Inf)
    • prior_amp = Unif(0, Inf)
    • prior_pos = Unif(100, 900)
    • prior_lwidth = Unif(0, Inf)
    • prior_gwidth = Unif(0, Inf)
  • pos, lwidth, gwidth prior can be chosen arbitrarily; not done here due to time constraints
max_a <- 1e-2
lower <- c(-Inf, -max_a, 0) #lower_b, lower_a, lower_amp; encodes lower bounds of prior.Unif
upper <- c(Inf, max_a, Inf) #upper_b, upper_a, upper_amp; encodes lower bounds of prior.Unif

start_params <- prop.voigt.rnd_start(seed = 33, kp = fit_kp)

res <- chains.collapsed_slicer_gibbs(
        x, y, signal_model,
        start_params, lower, upper, noise_sigma,
        iter = 1000, print_bar = TRUE, half_steps = TRUE, decorr_trafo = TRUE
)

2.1 Results

2.1.1 Fit

2.1.1.1 All

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

2.1.1.2 Burn-in

plt.fit.chain.interactive(x, y, res$samples[seq(1, 40)], signal_model, step_width = 2)

2.1.2 Traceplots

plt.fit.chain.interactive(x, y, res$samples, signal_model, step_width = 10)
plt.traceplot(res$samples, "pos")
plt.traceplot(res$samples, "amp")
plt.traceplot(res$samples, "lwidth")
plt.traceplot(res$samples, "gwidth")
plt.traceplot(res$samples, "a")
plt.traceplot(res$samples, "b")

3 RJMCMC-burnin Gibbs Sampler

Gibbs Sampler starts by fitting one peak and adds a peak every add_peak_every iterations until fit_kp is reached at which point it just becomes a normal gibbs sampler. Helps resolving the peak deconvolution problem seen above and is an strong hint on the power of a full RJMCMC implementation.

  • Better fit than EM
  • Peak deconvolution (pos[1] and pos[7]) takes ages to resolve
peak_spawner <- function(){
  params <- list(
          amp = rnorm(1, 1, 0.001),
          lwidth = rnorm(1, 0.1, 0.001),
          gwidth = rnorm(1, 4, 0.001),
          pos = runif(1, 100, 900),
          a = 0,
          b = 0
  )
  return(params)
}

res <- chains.rjmcmc_like_collapsed_slicer_gibbs(
        x,
        y,
        signal_model,
        peak_spawner,
        lower,
        upper,
        noise_sigma,
        fit_kp = fit_kp,
        add_peak_every = 10,
        iter = 5000,
        print_bar = TRUE,
        half_steps = TRUE,
        decorr_trafo = TRUE,
)

3.1 Results

3.1.1 Fit

3.1.1.1 All

plt.fit.chain.interactive(x, y, res_rjmcmc$samples, signal_model, step_width = 40)

3.1.1.2 Burn-in

plt.fit.chain.interactive(x, y, res_rjmcmc$samples[seq(1, 100)], signal_model, step_width = 2)

3.1.2 Traceplots

plt.traceplot(res_rjmcmc$samples, "pos")
plt.traceplot(res_rjmcmc$samples, "amp")
plt.traceplot(res_rjmcmc$samples, "lwidth")
plt.traceplot(res_rjmcmc$samples, "gwidth")
plt.traceplot(res_rjmcmc$samples, "a")
plt.traceplot(res_rjmcmc$samples, "b")

4 Todo

  • Critical Bug amp, a, b are not sampled instead they are set to roughly their means (not really either); Easy to fix.

  • Not a bug: Uses a safeguard which prevents messing up the fit if the slice sampler fails, this however destroys the gibbs sampler property and causes rejection plateaus.

  • Done: nu, be transformation becomes bad (why?) if either lwidth or gwidth is really small compared to the other, should switch to nu,be only if min(lwidth/gwidth, gwidth/lwidth) > 0.05

  • Peak deconvolution is the central problem (i.e. fitting strongly overlapping peaks). See the second fit where only 1 Peak is misplaced. RJMCMC would most likely instantly solve this problem as it would add an peak on the right flank shortly increasing \(kp\) to 10 and then removing the misplaced peak putting \(kp\) to 9 again. This is an massive advantage that seems to be impossible to model with fixed \(kp\). If a given \(kp\) is needed the \(kp\) prior could be slowly changed to degenerate to only the given value of \(kp\).

  • Done prop.voigt.rnd_start spawns to wide peaks for gibbs (the tuning for the spawner is very impactful)

  • Do Speed ups Develop heuristic to evaluate pmvnorm fast; better slice sampler; better early termination of slice sampler. Run profiler to figure out if pmvnorm is actually the bottleneck or X construction. If it is X could use caching and speed up code by 90%


  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ß) ↩︎

LS0tDQp0aXRsZTogIkNvbGxhcHNlZCBHaWJicyBTYW1wbGVyIHdpdGggU2xpY2UgU2FtcGxpbmciDQphdXRob3I6IEphbiBNZWnDn25lcl5bUldUSCBBYWNoZW4sIHBoaWxpcHAubWVpc3NuZXJAcnd0aC1hYWNoZW4uZGVdDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6DQogICAgdG9jOiB0cnVlDQogICAgdG9jX2RlcHRoOiA1DQogICAgdG9jX2Zsb2F0Og0KICAgICAgY29sbGFwc2VkOiBmYWxzZQ0KICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQ0KLS0tDQoNCjxzY3JpcHQgdHlwZT0idGV4dC9qYXZhc2NyaXB0Ij4NCiAgZnVuY3Rpb24ganVtcF9oZWFkZXIoa2V5KXsNCiAgICAkKCc6aGVhZGVyOmNvbnRhaW5zKCcra2V5KycpJylbMF0uc2Nyb2xsSW50b1ZpZXcoKTsNCiAgfQ0KICANCiAgZnVuY3Rpb24ganVtcF9tYXJrZWQoa2V5KXsNCiAgICAkKCcjJyArIGtleSlbMF0uc2Nyb2xsSW50b1ZpZXcoKTsNCiAgfQ0KDQo8L3NjcmlwdD4NCioqUkpNQ01DLWJ1cm5pbiBHaWJicyBTYW1wbGVyIDxhIG9uY2xpY2s9J2p1bXBfbWFya2VkKCJleGFjdGdpYmJzIik7Jz4oZml0cyBleGFjdGx5KTwvYT4gPj4gR2liYnMgU2FtcGxlcioqDQoNCiMgRGF0YQ0KDQoNCmBgYHtyfQ0KbGlicmFyeShzcGVjdHJhbG1jKQ0KDQp0cnVlX2twIDwtIDkNCmZpdF9rcCA8LSA5DQpub2lzZV9zaWdtYSA8LSAwLjAwMQ0Kc2lnbmFsX21vZGVsIDwtIHZvaWd0Lm1vZGVsDQoNCmRhdGEgPC0gZGF0YS5zeW50aGV0aWMuc3Rvcm15Y2xvdWRzKHNlZWQgPSA1MzI1Miwga3AgPSB0cnVlX2twLCBub2lzZSA9IDAuMDAxKQ0KeCA8LSBkYXRhJHgNCnkgPC0gZGF0YSR5DQpgYGANCg0KIyBHaWJicyBTYW1wbGVyDQpVc2VzIGEgY29sbGFwc2VkIChtYXJnaW5hbGl6ZXMgb3ZlciBgYWAsIGBiYCwgYGFtcGAgbnVtZXJpY2FsbHkpDQpnaWJicyBzYW1wbGVyIHdoaWNoIGFwcHJveGltYXRlcyB0aGUgY29uZGl0aW9uYWxzIHdpdGggc2xpY2Ugc2FtcGxpbmcuIEFsc28gcmUtcGFyYW1ldGVyaXplcyBkZW5zaXR5IHdpdGggYChzcXJ0KGd3aWR0aF4yK2x3aWR0aF4yKSwgZ3dpZHRoLWx3aWR0aClgIGluc3RlYWQgb2YgYChnd2lkdGgsIGx3aXRkaClgIGlmIGBtaW4oZ3dpZHRoL2x3aWR0aCwgbHdpZHRoL2d3aWR0aCkgPiAwLjA1YCAob3RoZXJ3aXNlIHRyYW5zZm9ybWF0aW9uIGJlY29tZXMgdW5zdGFibGUpLiBVc2VzIGEgTGlrZWxpaG9vZC1yYXRpbyB0ZXN0IHRvIHN0b3Agc2xpY2Ugc2FtcGxpbmcgZWFybHkuIFNsaWNlIHNhbXBsaW5nIHJlcnVuIGEgZmV3IHRpbWVzICgzLTUpIHRvIGdlbmVyYXRlIGRyYXdzIGZyb20gd2hpY2ggb25lIGlzIHNhbXBsZWQgYWNjb3JkaW5nIHRvIHRoZWlyIHBvc3RlcmlvciBkZW5zaXR5IChwcmV2ZW50cyBzbGljZSBzYW1wbGluZyBmYWlsdXJlKS4NCg0KLSBUYWtlcyAxLTRzIHBlciBwZWFrIGFuZCBpdGVyYXRpb24NCiAgLSAqKjMwLTQwaCBydW50aW1lKiogZm9yIHRoaXMgbm90ZWJvb2sNCiAgLSBUaHVzIG9ubHkgdXNhYmxlIHdpdGggYSByZW1vdGUgY2x1c3RlciAoSSB3b250IHVzZSBhbnltb3JlIG9mIG15IGdvb2dsZS5jb2xhYiBxdW90YSBmb3IgdGhpcykNCi0gc2hvcnQgYnVybi1pbg0KLSBzdGFydF9wb2ludCBpcyBlc3NlbnRpYWxseSBpcnJlbGV2YW50IChtb2R1bG8gdGhlIGxhYmVsIHN3aXRjaGluZyBwcm9ibGVtLCBnZWxtYW4tcnViaW4gd2lsbCBiZSB0ZXJyaWJsZSBkdWUgdG8gdGhhdCkNCi0gc21hbGwgYXV0by1jb3JyZWxhdGlvbiAobG9va3MgbGlrZSBpdCBpbiB0cmFjZXMgYXQgbGVhc3QpDQotIGZpdHMgb2sNCi0gcHJvcG9zYWwgZnJlZQ0KLSBub24taW5mb3JtYXRpdmUgcHJpb3JzDQogIC0gcHJpb3JfYSA9IFVuaWYoMWUtMiwgMWUyKQ0KICAtIHByaW9yX2IgPSBVbmlmKC1JbmYsIEluZikNCiAgLSBwcmlvcl9hbXAgPSBVbmlmKDAsIEluZikNCiAgLSBwcmlvcl9wb3MgPSBVbmlmKDEwMCwgOTAwKQ0KICAtIHByaW9yX2x3aWR0aCA9IFVuaWYoMCwgSW5mKQ0KICAtIHByaW9yX2d3aWR0aCA9IFVuaWYoMCwgSW5mKQ0KLSBwb3MsIGx3aWR0aCwgZ3dpZHRoIHByaW9yIGNhbiBiZSBjaG9zZW4gYXJiaXRyYXJpbHk7IG5vdCBkb25lIGhlcmUgZHVlIHRvIHRpbWUgY29uc3RyYWludHMNCmBgYHtyIGV2YWwgPSBGQUxTRX0NCm1heF9hIDwtIDFlLTINCmxvd2VyIDwtIGMoLUluZiwgLW1heF9hLCAwKSAjbG93ZXJfYiwgbG93ZXJfYSwgbG93ZXJfYW1wOyBlbmNvZGVzIGxvd2VyIGJvdW5kcyBvZiBwcmlvci5VbmlmDQp1cHBlciA8LSBjKEluZiwgbWF4X2EsIEluZikgI3VwcGVyX2IsIHVwcGVyX2EsIHVwcGVyX2FtcDsgZW5jb2RlcyBsb3dlciBib3VuZHMgb2YgcHJpb3IuVW5pZg0KDQpzdGFydF9wYXJhbXMgPC0gcHJvcC52b2lndC5ybmRfc3RhcnQoc2VlZCA9IDMzLCBrcCA9IGZpdF9rcCkNCg0KcmVzIDwtIGNoYWlucy5jb2xsYXBzZWRfc2xpY2VyX2dpYmJzKA0KICAgICAgICB4LCB5LCBzaWduYWxfbW9kZWwsDQogICAgICAgIHN0YXJ0X3BhcmFtcywgbG93ZXIsIHVwcGVyLCBub2lzZV9zaWdtYSwNCiAgICAgICAgaXRlciA9IDEwMDAsIHByaW50X2JhciA9IFRSVUUsIGhhbGZfc3RlcHMgPSBUUlVFLCBkZWNvcnJfdHJhZm8gPSBUUlVFDQopDQpgYGANCmBgYHtyICBlY2hvPUZBTFNFfQ0Kc2V0d2QoJ0M6L1VzZXJzL0phbi9EZXNrdG9wL0lTV19TcGVjdHJhQmF5ZXMvSVNXX1NwZWN0cmFCYXllcy9iYXllc19tY21jJykNCnJlcyA8LSBsaXN0KCkNCnJlcyRzYW1wbGVzX3dpdGhfaGFsZiA8LSByZWFkUkRTKCJub3RlYm9va3MvY29sbGFwc2VkX3NsaWNlcl90cmFuc2Zvcm1lZF9naWJicy9zYW1wbGVzXzUwMF9ub1JKTUNNQy5SRGF0YSIpDQpyZW1vdmVfaGFsZl9zdGVwcyA8LSBmdW5jdGlvbihzYW1wbGVzLCBmaXRfa3ApIHsNCiAgc2FtcGxlc1tzZXEoZnJvbT0xLCB0bz1sZW5ndGgoc2FtcGxlcyksIGJ5PWZpdF9rcCArIDEpXQ0KfQ0KcmVzJHNhbXBsZXMgPC0gcmVtb3ZlX2hhbGZfc3RlcHMocmVzJHNhbXBsZXMsIGZpdF9rcCA9IGZpdF9rcCkNCg0KYGBgDQojIyBSZXN1bHRzDQoNCiMjIyBGaXQNCg0KIyMjIyBBbGwNCmBgYHtyfQ0KcGx0LmZpdC5jaGFpbi5pbnRlcmFjdGl2ZSh4LCB5LCByZXMkc2FtcGxlcywgc2lnbmFsX21vZGVsLCBzdGVwX3dpZHRoID0gNTApDQpgYGANCg0KIyMjIyBCdXJuLWluDQpgYGB7cn0NCnBsdC5maXQuY2hhaW4uaW50ZXJhY3RpdmUoeCwgeSwgcmVzJHNhbXBsZXNbc2VxKDEsIDQwKV0sIHNpZ25hbF9tb2RlbCwgc3RlcF93aWR0aCA9IDIpDQpgYGANCiMjIyBUcmFjZXBsb3RzDQpgYGB7cn0NCnBsdC5maXQuY2hhaW4uaW50ZXJhY3RpdmUoeCwgeSwgcmVzJHNhbXBsZXMsIHNpZ25hbF9tb2RlbCwgc3RlcF93aWR0aCA9IDEwKQ0KYGBgDQpgYGB7cn0NCnBsdC50cmFjZXBsb3QocmVzJHNhbXBsZXMsICJwb3MiKQ0KcGx0LnRyYWNlcGxvdChyZXMkc2FtcGxlcywgImFtcCIpDQpwbHQudHJhY2VwbG90KHJlcyRzYW1wbGVzLCAibHdpZHRoIikNCnBsdC50cmFjZXBsb3QocmVzJHNhbXBsZXMsICJnd2lkdGgiKQ0KcGx0LnRyYWNlcGxvdChyZXMkc2FtcGxlcywgImEiKQ0KcGx0LnRyYWNlcGxvdChyZXMkc2FtcGxlcywgImIiKQ0KYGBgDQojIFJKTUNNQy1idXJuaW4gR2liYnMgU2FtcGxlcg0KR2liYnMgU2FtcGxlciBzdGFydHMgYnkgZml0dGluZyBvbmUgcGVhayBhbmQgYWRkcyBhIHBlYWsgZXZlcnkgYGFkZF9wZWFrX2V2ZXJ5YCBpdGVyYXRpb25zIHVudGlsIGBmaXRfa3BgIGlzIHJlYWNoZWQgYXQgd2hpY2ggcG9pbnQgaXQganVzdCBiZWNvbWVzIGEgbm9ybWFsIGdpYmJzIHNhbXBsZXIuIEhlbHBzIHJlc29sdmluZyB0aGUgcGVhayBkZWNvbnZvbHV0aW9uIHByb2JsZW0gc2VlbiBhYm92ZSBhbmQgaXMgYW4gc3Ryb25nIGhpbnQgb24gdGhlIHBvd2VyIG9mIGEgZnVsbCBSSk1DTUMgaW1wbGVtZW50YXRpb24uDQoNCi0gQmV0dGVyIGZpdCB0aGFuIEVNDQotIFBlYWsgZGVjb252b2x1dGlvbiAoYHBvc1sxXWAgYW5kIGBwb3NbN11gKSB0YWtlcyBhZ2VzIHRvIHJlc29sdmUNCmBgYHtyIGV2YWwgPSBGQUxTRX0NCnBlYWtfc3Bhd25lciA8LSBmdW5jdGlvbigpew0KICBwYXJhbXMgPC0gbGlzdCgNCiAgICAgICAgICBhbXAgPSBybm9ybSgxLCAxLCAwLjAwMSksDQogICAgICAgICAgbHdpZHRoID0gcm5vcm0oMSwgMC4xLCAwLjAwMSksDQogICAgICAgICAgZ3dpZHRoID0gcm5vcm0oMSwgNCwgMC4wMDEpLA0KICAgICAgICAgIHBvcyA9IHJ1bmlmKDEsIDEwMCwgOTAwKSwNCiAgICAgICAgICBhID0gMCwNCiAgICAgICAgICBiID0gMA0KICApDQogIHJldHVybihwYXJhbXMpDQp9DQoNCnJlcyA8LSBjaGFpbnMucmptY21jX2xpa2VfY29sbGFwc2VkX3NsaWNlcl9naWJicygNCiAgICAgICAgeCwNCiAgICAgICAgeSwNCiAgICAgICAgc2lnbmFsX21vZGVsLA0KICAgICAgICBwZWFrX3NwYXduZXIsDQogICAgICAgIGxvd2VyLA0KICAgICAgICB1cHBlciwNCiAgICAgICAgbm9pc2Vfc2lnbWEsDQogICAgICAgIGZpdF9rcCA9IGZpdF9rcCwNCiAgICAgICAgYWRkX3BlYWtfZXZlcnkgPSAxMCwNCiAgICAgICAgaXRlciA9IDUwMDAsDQogICAgICAgIHByaW50X2JhciA9IFRSVUUsDQogICAgICAgIGhhbGZfc3RlcHMgPSBUUlVFLA0KICAgICAgICBkZWNvcnJfdHJhZm8gPSBUUlVFLA0KKQ0KYGBgDQpgYGB7ciAgZWNobz1GQUxTRX0NCnJlbW92ZV9oYWxmX3N0ZXBzX3JqbWNtYyA8LSBmdW5jdGlvbih0cnVlX3NhbXBsZXMsIGZpdF9rcCwgYWRkX3BlYWtfZXZlcnksIGl0ZXIgPSA1MDAwLCBoYWxmX3N0ZXBzID0gVFJVRSkgew0KICByZXNfbGlzdCA8LSBsaXN0KCkNCiAgdG90X2luZGV4IDwtIDANCg0KICBjdXJyIDwtIGxpc3QocG9zID0gYygwKSkNCiAgc2FtcGxlcyA8LSBsaXN0KCkNCiAgIyMjIyMNCiAgdG90X2luZGV4IDwtIHRvdF9pbmRleCArIDENCiAgaWYgKGxlbmd0aCh0cnVlX3NhbXBsZXMpPHRvdF9pbmRleCkge3JldHVybihyZXNfbGlzdCl9DQogIHJlc19saXN0IDwtIGMocmVzX2xpc3QsIGxpc3QodHJ1ZV9zYW1wbGVzW1t0b3RfaW5kZXhdXSkpDQogICMjIyMjDQogIHNhbXBsZXMgPC0gYyhzYW1wbGVzLCBsaXN0KGN1cnIpKQ0KICBmb3IgKGkgaW4gc2VxKGl0ZXIpKSB7DQoNCiAgICBpZiAoaSAlJSBhZGRfcGVha19ldmVyeSA9PSAwICYmIGkgPiAwICYmIGxlbmd0aChjdXJyJHBvcykgPCBmaXRfa3Apew0KICAgICAgY3VyciRwb3MgPC0gYyhjdXJyJHBvcywgMCkNCiAgICB9DQoNCiAgICBrcCA8LSBsZW5ndGgoY3VyciRwb3MpDQogICAgZm9yIChrIGluIHNlcShrcCkpIHsNCiAgICAgIGlmIChoYWxmX3N0ZXBzKSB7DQogICAgICAgICMjIyMjDQogICAgICAgIHRvdF9pbmRleCA8LSB0b3RfaW5kZXggKyAxDQogICAgICAgIGlmIChsZW5ndGgodHJ1ZV9zYW1wbGVzKTx0b3RfaW5kZXgpIHtyZXR1cm4ocmVzX2xpc3QpfQ0KICAgICAgICAjcmVzX2xpc3QgPC0gYyhyZXNfbGlzdCwgbGlzdCh0cnVlX3NhbXBsZXNbW3RvdF9pbmRleF1dKSkNCiAgICAgICAgIyMjIyMNCiAgICAgICAgc2FtcGxlcyA8LSBjKHNhbXBsZXMsIGxpc3QoY3VycikpDQogICAgICB9ICNvbmx5IGlmIGhhbGYgc3RlcHMNCiAgICB9DQogICAgIyMjIyMNCiAgICB0b3RfaW5kZXggPC0gdG90X2luZGV4ICsgMQ0KICAgIGlmIChsZW5ndGgodHJ1ZV9zYW1wbGVzKTx0b3RfaW5kZXgpIHtyZXR1cm4ocmVzX2xpc3QpfQ0KICAgIHJlc19saXN0IDwtIGMocmVzX2xpc3QsIGxpc3QodHJ1ZV9zYW1wbGVzW1t0b3RfaW5kZXhdXSkpDQogICAgIyMjIyMNCiAgICBzYW1wbGVzIDwtIGMoc2FtcGxlcywgbGlzdChjdXJyKSkNCiAgfQ0KICByZXR1cm4odHJ1ZV9zYW1wbGVzKQ0KfQ0KDQpyZW1vdmVfaGFsZl9zdGVwcyA8LSBmdW5jdGlvbihzYW1wbGVzLCBmaXRfa3ApIHsNCiAgc2FtcGxlc1tzZXEoZnJvbT0xLCB0bz1sZW5ndGgoc2FtcGxlcyksIGJ5PWZpdF9rcCArIDEpXQ0KfQ0KDQpwYWRfd2l0aF96ZXJvcyA8LSBmdW5jdGlvbihzYW1wbGVzLCBmaXRfa3Apew0KICBmb3IgKGkgaW4gc2VxX2Fsb25nKHNhbXBsZXMpKXsNCiAgICBzYW1wbGUgPC0gc2FtcGxlc1tbaV1dDQogICAgZm9yIChuYW1lIGluIG5hbWVzKHNhbXBsZSkpew0KICAgICAgaWYgKG5hbWUgIT0gJ2EnICYmIG5hbWUgIT0gJ2InICYmIG5hbWUgIT0gJ2FtcCcpew0KICAgICAgICBzYW1wbGVbW25hbWVdXSA8LSBjKHJlcCgxLCBmaXRfa3AtbGVuZ3RoKHNhbXBsZVtbbmFtZV1dKSksIHNhbXBsZVtbbmFtZV1dKQ0KICAgICAgfQ0KICAgICAgaWYgKG5hbWUgPT0gJ2FtcCcpew0KICAgICAgICBzYW1wbGVbW25hbWVdXSA8LSBjKHJlcCgwLCBmaXRfa3AtbGVuZ3RoKHNhbXBsZVtbbmFtZV1dKSksIHNhbXBsZVtbbmFtZV1dKQ0KICAgICAgfQ0KICAgIH0NCiAgICBzYW1wbGVzW1tpXV0gPC0gc2FtcGxlDQogIH0NCiAgc2FtcGxlcw0KfQ0Kc2V0d2QoJ0M6L1VzZXJzL0phbi9EZXNrdG9wL0lTV19TcGVjdHJhQmF5ZXMvSVNXX1NwZWN0cmFCYXllcy9iYXllc19tY21jJykNCnJlc19yam1jbWMgPC0gbGlzdCgpDQpyZXNfcmptY21jJHNhbXBsZXNfd2l0aF9oYWxmIDwtIHJlYWRSRFMoIm5vdGVib29rcy9jb2xsYXBzZWRfc2xpY2VyX3RyYW5zZm9ybWVkX2dpYmJzL3NhbXBsZXNfMV8ya18xMDQ0LlJEYXRhIikNCnJlc19yam1jbWMkc2VjIDwtIHJlYWRSRFMoIm5vdGVib29rcy9jb2xsYXBzZWRfc2xpY2VyX3RyYW5zZm9ybWVkX2dpYmJzL3NhbXBsZXMuUkRhdGEiKQ0KDQpyZXNfcmptY21jJHNhbXBsZXMxIDwtIHJlbW92ZV9oYWxmX3N0ZXBzX3JqbWNtYyhyZXNfcmptY21jJHNhbXBsZXNfd2l0aF9oYWxmLCBmaXRfa3AgPSBmaXRfa3AsIGFkZF9wZWFrX2V2ZXJ5ID0gMTAsIGl0ZXIgPSAxMDAwMCwgaGFsZl9zdGVwcyA9IFRSVUUpDQpyZXNfcmptY21jJHNhbXBsZXMyIDwtIHJlbW92ZV9oYWxmX3N0ZXBzKHJlc19yam1jbWMkc2VjLCBmaXRfa3AgPSBmaXRfa3ApDQoNCnJlc19yam1jbWMkc2FtcGxlczEgPC0gcGFkX3dpdGhfemVyb3MocmVzX3JqbWNtYyRzYW1wbGVzMSwgZml0X2twID0gZml0X2twKQ0KcmVzX3JqbWNtYyRzYW1wbGVzMiA8LSBwYWRfd2l0aF96ZXJvcyhyZXNfcmptY21jJHNhbXBsZXMyLCBmaXRfa3AgPSBmaXRfa3ApDQoNCnJlc19yam1jbWMkc2FtcGxlcyA8LSBjKHJlc19yam1jbWMkc2FtcGxlczEsIHJlc19yam1jbWMkc2FtcGxlczJbc2VxKDIsIGxlbmd0aChyZXNfcmptY21jJHNhbXBsZXMyKSldKQ0KDQojIDEtMmsgaGFzIDI0aCBleGVjdXRpb24gYnV0IGR1ZSB0byBpbmZlcl9jIHNsb3dlciB0YWtlIDE1aA0KYGBgDQojIyBSZXN1bHRzDQoNCiMjIyBGaXQNCiMjIyMgQWxsDQo8ZGl2IGlkPSJleGFjdGdpYmJzIj48L2Rpdj4NCmBgYHtyfQ0KcGx0LmZpdC5jaGFpbi5pbnRlcmFjdGl2ZSh4LCB5LCByZXNfcmptY21jJHNhbXBsZXMsIHNpZ25hbF9tb2RlbCwgc3RlcF93aWR0aCA9IDQwKQ0KYGBgDQojIyMjIEJ1cm4taW4NCmBgYHtyfQ0KcGx0LmZpdC5jaGFpbi5pbnRlcmFjdGl2ZSh4LCB5LCByZXNfcmptY21jJHNhbXBsZXNbc2VxKDEsIDEwMCldLCBzaWduYWxfbW9kZWwsIHN0ZXBfd2lkdGggPSAyKQ0KYGBgDQojIyMgVHJhY2VwbG90cw0KYGBge3J9DQpwbHQudHJhY2VwbG90KHJlc19yam1jbWMkc2FtcGxlcywgInBvcyIpDQpwbHQudHJhY2VwbG90KHJlc19yam1jbWMkc2FtcGxlcywgImFtcCIpDQpwbHQudHJhY2VwbG90KHJlc19yam1jbWMkc2FtcGxlcywgImx3aWR0aCIpDQpwbHQudHJhY2VwbG90KHJlc19yam1jbWMkc2FtcGxlcywgImd3aWR0aCIpDQpwbHQudHJhY2VwbG90KHJlc19yam1jbWMkc2FtcGxlcywgImEiKQ0KcGx0LnRyYWNlcGxvdChyZXNfcmptY21jJHNhbXBsZXMsICJiIikNCmBgYA0KDQojIFRvZG8NCi0gKipDcml0aWNhbCBCdWcqKiBhbXAsIGEsIGIgYXJlIG5vdCBzYW1wbGVkIGluc3RlYWQgdGhleSBhcmUgc2V0IHRvIHJvdWdobHkgdGhlaXIgbWVhbnMgKG5vdCByZWFsbHkgZWl0aGVyKTsgRWFzeSB0byBmaXguDQotICoqTm90IGEgYnVnKio6IFVzZXMgYSBzYWZlZ3VhcmQgd2hpY2ggcHJldmVudHMgbWVzc2luZyB1cCB0aGUgZml0IGlmIHRoZSBzbGljZSBzYW1wbGVyIGZhaWxzLCB0aGlzIGhvd2V2ZXIgZGVzdHJveXMgdGhlIGdpYmJzIHNhbXBsZXIgcHJvcGVydHkgYW5kIGNhdXNlcyByZWplY3Rpb24gcGxhdGVhdXMuDQotICoqRG9uZSoqOiBudSwgYmUgdHJhbnNmb3JtYXRpb24gYmVjb21lcyBiYWQgKHdoeT8pIGlmIGVpdGhlciBsd2lkdGggb3IgZ3dpZHRoIGlzIHJlYWxseSBzbWFsbCBjb21wYXJlZCB0byB0aGUgb3RoZXIsIHNob3VsZCBzd2l0Y2ggdG8gbnUsYmUgb25seSBpZiBgbWluKGx3aWR0aC9nd2lkdGgsIGd3aWR0aC9sd2lkdGgpID4gMC4wNWANCjwhLS0NCi0gVW5jbGVhbjogSW5zdGVhZCBvZiB0cnlpbmcgdG8gcmVndWxhcml6ZSB0aGUgLUluZiBpbiBsb2dwcm9icyBhd2F5IGp1c3QgcmVqZWN0IHRoZSBzbGljZSBzYW1wbGVyIGFuZCByZXJ1biB1bnRpbCBpdCBkb2VzIGZpbmQgYSBuZXcgcG9pbnQuDQotIEhvdyB0byBkZWFsIHdpdGggcGVhayBkZWdlbmVyYWN5PyBpLmUuIHNhbWUgcG9zIGd3aWR0aCBsd2lkdGggYnV0IGFtcHMgYXJlIHNwbGl0IGJldHdlZW4gdHdvIHBlYWtzIC0+IHJ1bnMgZGVlcGVyIHRoYW4gdGhpcyBjb25uZWN0ZWQgdG8gcGVhayBkZWNvbnZvbHV0aW9uIHByb2JsZW0gLT4gaW5jcmVhc2UgZml0X2twIHNsb3dseSBsaWtlIGluIEVNIChidXQgdGhlbiB0aGlzIGlzbnQgYSBnaWJicyBhbnltb3JlKQ0KLSBQZWFrIGRlY29udm9sdXRpb24gaXMgYWN0dWFsbHkgdGhlIGxhc3QgbWFzc2l2ZSBwcm9ibGVtLCB0aGF0IGlzIHdoZW4gcGVha3Mgc3Ryb25nbHkgaW50ZXJhY3Qgd2l0aCBlYWNoIG90aGVyIGFuZCB0aGUgcG9zdGVyaW9yIGJlY29tZXMgc3Ryb25nbHkgY29ycmVsYXRlZC4gVGhlIGdpYmJzIHNhbXBsZXIgYmVjb21lcyBhcyB1c3VhbCB2ZXJ5IHNsb3cuIE5vdCBwb3NzaWJsZSB0byBmaXggd2l0aCBoYXN0aW5ncyBhcyBwcm9wb3NhbCBpcyBpbXBvc3NpYmxlIHRvIGZpbmQuIEEgc3VicHJvYmxlbSBvZiB0aGF0IGlzIHRoYXQgc29tZXRpbWVzIGEgc2luZ2xlIHBlYWsgaXMgZml0IHdpdGggYXMgbWFueSBhcyAzIHBlYWtzLCBnaWJicyAoYW5kIGhhc3RpbmdzKSBjYW4ndCByZXNvbHZlIHRoaXMgaXNzdWUgZWFzaWx5LiBPbmUgcG9zc2libGUgd2F5IHRvIGZpeCB0aGlzIGlzIHRvIHNldCBhbXAgcHJpb3IgdGhhdCBwZW5hbGl6ZXMgbGFyZ2VyIGBhbXBzYDsgZ2F1c3NpYW4gdHJ1bmN0IHByaW9yIGNvdWxkIGJlIHVzZWQgYnV0IG5vdCB2ZXJ5IHNhdGlzZmFjdG9yeSBwcmlvciBvdGhlciBwcmlvcnMgY2FudCByZWFsbHkgYmUgdXNlZCBkdWUgdG8gY29sbGFwc2UNCi0gKipJZGVhOioqKCFhbXAgaXMgbm90IGhlaWdodCBvZiBwZWFrISBhbXAqVm9pZ3QoLi4uKSwgdm9pZ3QgaGFzIGFuIHdpZHRoIGRlcGVuZGVudCBoZWlnaHQgZmFjdG9yIGFzd2VsbCEpIFNldCBwcmlvciBvbiBhbXAgdG8gc29tZXRoaW5nIGxpa2UgJCRcbWF0aGNhbHtOfSh4IHwgLTUsIHNkPTEwKSAxX3t4IFxpbiBbMCwgXGluZnR5KX0gJCQgdG8gcGVuYWxpemUgbGFyZ2VyIGFtcHMgc3VjaCB0aGF0IHRoZXJlIGlzIGEgbG9jYWwgZ3JhZGllbnQgaW4gdGhlIHBvc3RlcmlvciB0aGF0IHJld2FyZHMgZml0dGluZ3MgcGVha3Mgd2l0aCBhcyBsaXR0bGUgcGVha3MgYXMgcG9zc2libGUuDQotLT4NCi0gUGVhayBkZWNvbnZvbHV0aW9uIGlzIHRoZSBjZW50cmFsIHByb2JsZW0gKGkuZS4gZml0dGluZyBzdHJvbmdseSBvdmVybGFwcGluZyBwZWFrcykuIFNlZSB0aGUgc2Vjb25kIGZpdCB3aGVyZSBvbmx5IDEgUGVhayBpcyBtaXNwbGFjZWQuIFJKTUNNQyB3b3VsZCBtb3N0IGxpa2VseSBpbnN0YW50bHkgc29sdmUgdGhpcyBwcm9ibGVtIGFzIGl0IHdvdWxkICphZGQqIGFuIHBlYWsgb24gdGhlIHJpZ2h0IGZsYW5rIHNob3J0bHkgaW5jcmVhc2luZyAka3AkIHRvIDEwIGFuZCB0aGVuIHJlbW92aW5nIHRoZSBtaXNwbGFjZWQgcGVhayBwdXR0aW5nICRrcCQgdG8gOSBhZ2Fpbi4gVGhpcyBpcyBhbiBtYXNzaXZlIGFkdmFudGFnZSB0aGF0IHNlZW1zIHRvIGJlIGltcG9zc2libGUgdG8gbW9kZWwgd2l0aCBmaXhlZCAka3AkLiBJZiBhIGdpdmVuICRrcCQgaXMgbmVlZGVkIHRoZSAka3AkIHByaW9yIGNvdWxkIGJlIHNsb3dseSBjaGFuZ2VkIHRvIGRlZ2VuZXJhdGUgdG8gb25seSB0aGUgZ2l2ZW4gdmFsdWUgb2YgJGtwJC4NCg0KLSAqKkRvbmUqKiBwcm9wLnZvaWd0LnJuZF9zdGFydCBzcGF3bnMgdG8gd2lkZSBwZWFrcyBmb3IgZ2liYnMgKHRoZSB0dW5pbmcgZm9yIHRoZSBzcGF3bmVyIGlzIHZlcnkgaW1wYWN0ZnVsKQ0KLSAqKkRvIFNwZWVkIHVwcyoqIERldmVsb3AgaGV1cmlzdGljIHRvIGV2YWx1YXRlIHBtdm5vcm0gZmFzdDsgYmV0dGVyIHNsaWNlIHNhbXBsZXI7IGJldHRlciBlYXJseSB0ZXJtaW5hdGlvbiBvZiBzbGljZSBzYW1wbGVyLiBSdW4gcHJvZmlsZXIgdG8gZmlndXJlIG91dCBpZiBwbXZub3JtIGlzIGFjdHVhbGx5IHRoZSBib3R0bGVuZWNrIG9yIFggY29uc3RydWN0aW9uLiBJZiBpdCBpcyBYIGNvdWxkIHVzZSBjYWNoaW5nIGFuZCBzcGVlZCB1cCBjb2RlIGJ5IDkwJQ0K