Below is the hands-on exercises on the advanced methods. This will cover the heuristic search, how to handle clustering and how to perform Bayesian model averaging.

Note: in the following text: Bayesian network, structure and DAG are synonyms.

Mixed models – correction for grouped data (or clustering)

Note: In the following: clustering and grouped are taken as synonym.

In some situations, the way the data were collected has a clear grouping aspect, and therefore there is a potential risk for non-independence between data points from the same group that could cause over-dispersion. This can lead to analyses which are over-optimistic as the true level of variation in the data is under-estimated. It could have an impact … or not. But this is a good practice to check!

In practice we will introduce a random effect to account for this additional variability. Thus each node will become a GLMM (Generalized Linear Mixed Model (Faraway, 2016)) instead of a GLM (Generalized Linear Model (McCullagh, 2018)) but in a Bayesian setting. We will then compute the posterior distribution and check if they widen. In such case, we will have to take into account the clustering in the scoring scheme we use.

In practice, the major problem is the computational complexity of this approach. Indeed, if the clustering do not affect too much the result, it is preferable to not take it into account. The model is then much simpler and parsimonious then more generalizable.

The grouping variable is farm and we apply the random effect to every nodes. On a personal computer following code takes 15 minutes where the previous code ran in less than a second! The grouping variable has 15 levels.

marg.f.grouped <- fitabn(dag.m = trim.dag, 
                 data.df = as.data.frame(abndata),
                 data.dists = dist, 
                 group.var = "farm",
                 cor.vars = c("AR", "pneumS", "female", "livdam", "eggs", "wormCount", "age", "adg"),
                 compute.fixed = TRUE, 
                 n.grid = 1000)

Visually inspect the marginal posterior distributions of the parameters

Calculate the area under the density curve

auc
AR|(Intercept) 0.9999986
AR|age 0.9999994
AR|group.precision 0.9999853
pneumS|(Intercept) 0.9999977
pneumS|group.precision 0.9999532
female|(Intercept) 0.9999996
female|group.precision 0.9992713
livdam|(Intercept) 0.9999978
livdam|eggs 0.9999976
livdam|group.precision 0.9999974
eggs|(Intercept) 0.9999957
eggs|adg 0.9999990
eggs|group.precision 0.9999999
wormCount|(Intercept) 0.9999972
wormCount|AR 0.9999995
wormCount|eggs 0.9999991
wormCount|age 0.9999997
wormCount|adg 0.9999997
wormCount|group.precision 0.9999976
age|(Intercept) 0.9999984
age|female 0.9999996
age|group.precision 0.9999982
age|precision 0.9999995
adg|(Intercept) 0.9999993
adg|age 0.9999997
adg|group.precision 0.9999725
adg|precision 0.9999980

 

Here the shape of some of the parameter distribution looks weird. Indeed, there is a deal of information between the random strata. However, the AUC looks good. So we can proceed getting the estimates.

 

Get the table of quantiles for the marginals

As one can see, except for a precision parameter (female|group.precision) the quantiles seems to not have too much widen.

Non-adjusted marginal densities
2.5% 50% 97.5%
AR|(Intercept) 0.891 1.155 1.435
AR|age 0.509 0.798 1.110
pneumS|(Intercept) -2.134 -1.814 -1.519
female|(Intercept) -0.196 0.018 0.230
livdam|(Intercept) 0.976 1.265 1.567
livdam|eggs 0.663 1.547 2.656
eggs|(Intercept) -1.508 -1.232 -0.971
eggs|adg 0.346 0.614 0.895
wormCount|(Intercept) -1.646 -1.400 -1.170
wormCount|AR 0.152 0.273 0.395
wormCount|eggs 3.280 3.504 3.741
wormCount|age -0.824 -0.685 -0.548
wormCount|adg -0.374 -0.240 -0.107
age|(Intercept) -0.349 -0.200 -0.052
age|female 0.188 0.397 0.606
age|precision 0.893 1.042 1.207
adg|(Intercept) -0.053 0.000 0.053
adg|age -0.921 -0.868 -0.816
adg|precision 3.485 4.070 4.712
Marginal densities adjusted with random effects
2.5% 50% 97.5%
AR|(Intercept) 0.761 1.236 1.766
AR|age 0.371 0.758 1.163
AR|group.precision 0.633 2.105 10.084
pneumS|(Intercept) -2.664 -2.006 -1.521
pneumS|group.precision 0.533 2.339 12.875
female|(Intercept) -0.209 0.025 0.256
female|group.precision 166.132 2333.598 4707.719
livdam|(Intercept) 1.067 1.826 2.748
livdam|eggs -0.116 1.040 2.346
livdam|group.precision 0.229 0.666 1.762
eggs|(Intercept) -3.071 -1.687 -0.413
eggs|adg -0.344 0.100 0.544
eggs|group.precision 0.071 0.207 0.515
wormCount|(Intercept) -2.562 -1.756 -1.043
wormCount|AR 0.325 0.452 0.579
wormCount|eggs 2.428 2.700 2.980
wormCount|age -0.266 -0.093 0.080
wormCount|adg -0.244 -0.100 0.044
wormCount|group.precision 0.240 0.631 1.425
age|(Intercept) -0.629 -0.221 0.184
age|female 0.312 0.446 0.579
age|group.precision 0.816 1.854 3.580
age|precision 2.214 2.594 3.012
adg|(Intercept) -0.162 -0.017 0.125
adg|age -0.904 -0.838 -0.772
adg|group.precision 6.610 15.856 33.294
adg|precision 4.706 5.514 6.402

MCMC over the structures

Usually, the output of a Bayesian network analysis of a dataset ends-up with a single well adjusted DAG. From the researcher point of view this could be frustrating. Indeed, the model is the one that is best supported by the data but the uncertainty quantification is missing. Classically in epidemiology, researchers are used to express point estimate with an uncertainty measure. An arc in a Bayesian network is a point estimate, we will see how to perform model averaging. The link strength measure is designed to account for that. An more natural alternative is to perform MCMC over structures (Friedman & Koller, 2003).

We use the mcmcabn() function on the cache of pre-computed networks scores. One needs to define the type of score used (here is the marginal likelihood mlik). The maximum of number of parents per node (same as the one used in buildscorecache()). The MCMC learning scheme, defined as: number of MCMC samples, number of thinned sampled (to avoid autocorrelation) and the length of the burn-in phase. Possibly a starting DAG and a structural prior. We also need to select the relative probability of performing radical moves (shuffling). Indeed, a naive MCMC approach is known to get very easily stuck in local maximum (for more details see: (Grzegorczyk & Husmeier, 2008; Su & Borsuk, 2016)).

mcmc.out <- mcmcabn(score.cache = mycache,
                  score = "mlik",
                  data.dists = dist,
                  max.parents = 4,
                  mcmc.scheme = c(1000,9,500),
                  seed = 321,
                  verbose = FALSE,
                  start.dag = "random",
                  prob.rev = 0.07,
                  prob.mbr = 0.07,
                  prior.choice = 1)

This is again computationally complex!

Her is a plot of the MCMC samples. One can see the scores of the structures on the y-axis in function of the index steps. The dots are the radical moves. One the right side a histogram shows the number of structures with a given score. As one can see the histogram is very peaked on the maximum possible score.

One can also display the cumulative maximum score (used for network score optimization).

But the major advantage of this method is the possibility of querying the MCMC sample using a formula statement.

# average individual arc support
query(mcmcabn = mcmc.out)
                  AR     pneumS     female     livdam       eggs
AR        0.00000000 0.01069893 0.01139886 0.01159884 0.07599240
pneumS    0.00589941 0.00000000 0.01539846 0.00829917 0.10398960
female    0.00579942 0.01879812 0.00000000 0.01769823 0.00659934
livdam    0.00689931 0.00999900 0.01119888 0.00000000 0.44275572
eggs      0.14908509 0.09239076 0.01019898 0.29377062 0.00000000
wormCount 0.76062394 0.08119188 0.03349665 0.07039296 0.91700830
age       0.13568643 0.07349265 0.15768423 0.11338866 0.09739026
adg       0.03019698 0.01589841 0.08359164 0.00959904 0.17288271
           wormCount        age        adg
AR        0.00349965 0.77212279 0.08649135
pneumS    0.00429957 0.19238076 0.03989601
female    0.00249975 0.29857014 0.12168783
livdam    0.00439956 0.06649335 0.01169883
eggs      0.02959704 0.25057494 0.36466353
wormCount 0.00000000 0.88481152 0.31056894
age       0.01399860 0.00000000 0.47095290
adg       0.00349965 0.52864714 0.00000000
# probability that worm count being linked to age but not to female directly
query(mcmcabn = mcmc.out,formula = ~wormCount|age-wormCount|female)
[1] 0.01889811
# probability that worm count being directly linked to age and adg and that adg is link to age (undirected)
query(mcmcabn = mcmc.out,formula = ~wormCount|age + wormCount|adg + age|adg)+
  query(mcmcabn = mcmc.out,formula = ~wormCount|age + wormCount|adg + adg|age)
[1] 0.2451755

References

Faraway, J. J. (2016). Extending the linear model with r: Generalized linear, mixed effects and nonparametric regression models. Chapman; Hall/CRC.

Friedman, N., & Koller, D. (2003). Being bayesian about network structure. a bayesian approach to structure discovery in bayesian networks. Machine Learning, 50(1-2), 95–125.

Grzegorczyk, M., & Husmeier, D. (2008). Improving the structure mcmc sampler for bayesian networks by introducing a new edge reversal move. Machine Learning, 71(2-3), 265.

Koivisto, M., & Sood, K. (2004). Exact bayesian structure discovery in bayesian networks. Journal of Machine Learning Research, 5(May), 549–573.

Korb, K. B., & Nicholson, A. E. (2010). Bayesian artificial intelligence. CRC press.

McCullagh, P. (2018). Generalized linear models. Routledge.

Su, C., & Borsuk, M. E. (2016). Improving structure mcmc for bayesian networks through markov blanket resampling. The Journal of Machine Learning Research, 17(1), 4042–4061.

LS0tCnRpdGxlOiAiSGFuZHMtb24gZXhlcmNpc2U6IGFkdmFuY2VkIG1ldGhvZHMiCmZvbnRzaXplOiAxMnB0Cm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB0cnVlCiAgICB0b2NfZGVwdGg6IDIKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKYmlibGlvZ3JhcGh5OiBiaWJfYWR2YW5jZWQuYmliCmNzbDogYXBhLmNzbAotLS0KCiZuYnNwOwoKCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IEZBTFNFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBjb2xsYXBzZT1GQUxTRSwKICAgICAgICAgICAgICAgICAgICAgIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLmhlaWdodD00LCBmaWcud2lkdGg9MTAsIGNvbW1lbnQgPSBOQSkKCm9wdGlvbnMoc2NpcGVuPTk5OSkKCnF1aWV0IDwtIGZ1bmN0aW9uKHgpIHsgCiAgc2luayh0ZW1wZmlsZSgpKSAKICBvbi5leGl0KHNpbmsoKSkgCiAgaW52aXNpYmxlKGZvcmNlKHgpKSAKfSAKCiMjIGxvYWQgZGF0YSwgbGlzdCBvZiBkaXN0IGV0YwpkdCA8LSByZWFkUkRTKCJkYXRhT0suUkRTIikKCmRyb3AgPC0gd2hpY2goY29sbmFtZXMoZHQpJWluJSBjKCJwbmV1bSIsICJlcGc1IiwgIndvcm1zIiwgImZhcm0iKSkKZHJvcC5jbHVzdHJpbmcgPC0gd2hpY2goY29sbmFtZXMoZHQpJWluJSBjKCJwbmV1bSIsICJlcGc1IiwgIndvcm1zIikpCgojI2VuZCBvZiBvbGQgc3R1ZmYKCmxpYnJhcnkoa25pdHIpCmxpYnJhcnkoa2FibGVFeHRyYSkKbGlicmFyeShhYm4pCmxpYnJhcnkobWNtY2FibikKYGBgCgpCZWxvdyBpcyB0aGUgaGFuZHMtb24gZXhlcmNpc2VzIG9uIHRoZSBhZHZhbmNlZCBtZXRob2RzLiBUaGlzIHdpbGwgY292ZXIgdGhlICoqaGV1cmlzdGljIHNlYXJjaCoqLCBob3cgdG8gaGFuZGxlICoqY2x1c3RlcmluZyoqIGFuZCBob3cgdG8gcGVyZm9ybSAqKkJheWVzaWFuIG1vZGVsIGF2ZXJhZ2luZyoqLgoKKk5vdGU6IGluIHRoZSBmb2xsb3dpbmcgdGV4dDogQmF5ZXNpYW4gbmV0d29yaywgc3RydWN0dXJlIGFuZCBEQUcgYXJlIHN5bm9ueW1zLioKCiMgTWl4ZWQgbW9kZWxzIOKAkyBjb3JyZWN0aW9uIGZvciBncm91cGVkIGRhdGEgKG9yIGNsdXN0ZXJpbmcpCgoqTm90ZTogSW4gdGhlIGZvbGxvd2luZzogY2x1c3RlcmluZyBhbmQgZ3JvdXBlZCBhcmUgdGFrZW4gYXMgc3lub255bS4qCgpJbiBzb21lIHNpdHVhdGlvbnMsIHRoZSB3YXkgdGhlIGRhdGEgd2VyZSBjb2xsZWN0ZWQgaGFzIGEgY2xlYXIgZ3JvdXBpbmcgYXNwZWN0LCBhbmQgdGhlcmVmb3JlIHRoZXJlIGlzIGEgcG90ZW50aWFsIHJpc2sgZm9yIG5vbi1pbmRlcGVuZGVuY2UgYmV0d2VlbiBkYXRhIHBvaW50cyBmcm9tIHRoZSBzYW1lIGdyb3VwIHRoYXQgY291bGQgY2F1c2Ugb3Zlci1kaXNwZXJzaW9uLiBUaGlzIGNhbiBsZWFkIHRvIGFuYWx5c2VzIHdoaWNoIGFyZSBvdmVyLW9wdGltaXN0aWMgYXMgdGhlIHRydWUgbGV2ZWwgb2YgdmFyaWF0aW9uIGluIHRoZSBkYXRhIGlzIHVuZGVyLWVzdGltYXRlZC4gSXQgY291bGQgaGF2ZSBhbiBpbXBhY3QgLi4uIG9yIG5vdC4gQnV0IHRoaXMgaXMgYSBnb29kIHByYWN0aWNlIHRvIGNoZWNrIQoKSW4gcHJhY3RpY2Ugd2Ugd2lsbCBpbnRyb2R1Y2UgYSByYW5kb20gZWZmZWN0IHRvIGFjY291bnQgZm9yIHRoaXMgYWRkaXRpb25hbCB2YXJpYWJpbGl0eS4gVGh1cyBlYWNoIG5vZGUgd2lsbCBiZWNvbWUgYSAqKkdMTU0qKiAoR2VuZXJhbGl6ZWQgTGluZWFyIE1peGVkIE1vZGVsIFtAZmFyYXdheTIwMTZleHRlbmRpbmddKSBpbnN0ZWFkIG9mIGEgKipHTE0qKiAoR2VuZXJhbGl6ZWQgTGluZWFyIE1vZGVsIFtAbWNjdWxsYWdoMjAxOGdlbmVyYWxpemVkXSkgYnV0IGluIGEgQmF5ZXNpYW4gc2V0dGluZy4gV2Ugd2lsbCB0aGVuIGNvbXB1dGUgdGhlIHBvc3RlcmlvciBkaXN0cmlidXRpb24gYW5kIGNoZWNrIGlmIHRoZXkgKip3aWRlbioqLiBJbiBzdWNoIGNhc2UsIHdlIHdpbGwgaGF2ZSB0byB0YWtlIGludG8gYWNjb3VudCB0aGUgY2x1c3RlcmluZyBpbiB0aGUgc2NvcmluZyBzY2hlbWUgd2UgdXNlLgoKSW4gcHJhY3RpY2UsIHRoZSBtYWpvciBwcm9ibGVtIGlzIHRoZSBjb21wdXRhdGlvbmFsIGNvbXBsZXhpdHkgb2YgdGhpcyBhcHByb2FjaC4gSW5kZWVkLCBpZiB0aGUgY2x1c3RlcmluZyBkbyBub3QgYWZmZWN0IHRvbyBtdWNoIHRoZSByZXN1bHQsIGl0IGlzIHByZWZlcmFibGUgdG8gbm90IHRha2UgaXQgaW50byBhY2NvdW50LiBUaGUgbW9kZWwgaXMgdGhlbiBtdWNoIHNpbXBsZXIgYW5kIHBhcnNpbW9uaW91cyB0aGVuIG1vcmUgZ2VuZXJhbGl6YWJsZS4gIAoKYGBge3J9CiMgaW5jb3Jwb3JhdGUgZ3JvdXBpbmcgZmFjdG9yIGluIHRoZSBkYXRhLgphYm5kYXRhIDwtIGR0WywgLWRyb3AuY2x1c3RyaW5nXQoKIyBzZXQgdXAgZmFjdG9ycwphYm5kYXRhWyxjKDE6NSw5KV0gPC0gYXMuZGF0YS5mcmFtZShsYXBwbHkoYWJuZGF0YVssYygxOjUsOSldLCBmYWN0b3IpKQoKIyByZWNvbXB1dGUgdGhlIHRyaW1lZCBEQUcgKGkuZS4gYXJjIHN1cHBvcnRlZCBieSBhdCBsZWFzdCA1MCUgb2YgdGhlIGJvb3RzdHJhcCBzYW1wbGVzKQpkYWdzIDwtIHJlYWRSRFMoIkJvb3REQUdzNTAwMC5SRFMiKQpkYWdzIDwtIGFycmF5KGRhdGEgPSB1bmxpc3QoZGFncyksZGltID0gYyg4LDgsNTAwMCkpCmRhZyA8LSBhcHBseShkYWdzLDE6MiwgbWVhbikKCnRyaW0uZGFnIDwtIGRhZwp0cmltLmRhZ1tkYWc+MC41XSA8LSAxCnRyaW0uZGFnW2RhZzw9MC41XSA8LSAwCgpjb2xuYW1lcyh0cmltLmRhZykgPC0gcm93bmFtZXModHJpbS5kYWcpIDwtIG5hbWVzKGRpc3QpCmBgYAoKVGhlIGdyb3VwaW5nIHZhcmlhYmxlIGlzIGBmYXJtYCBhbmQgd2UgYXBwbHkgdGhlIHJhbmRvbSBlZmZlY3QgdG8gZXZlcnkgbm9kZXMuIE9uIGEgcGVyc29uYWwgY29tcHV0ZXIgZm9sbG93aW5nIGNvZGUgdGFrZXMgMTUgbWludXRlcyB3aGVyZSB0aGUgcHJldmlvdXMgY29kZSByYW4gaW4gbGVzcyB0aGFuIGEgc2Vjb25kISBUaGUgZ3JvdXBpbmcgdmFyaWFibGUgaGFzIDE1IGxldmVscy4gCgpgYGB7ciwgZXZhbD1GQUxTRSwgZWNobz1UUlVFfQptYXJnLmYuZ3JvdXBlZCA8LSBmaXRhYm4oZGFnLm0gPSB0cmltLmRhZywgCiAgICAgICAgICAgICAgICAgZGF0YS5kZiA9IGFzLmRhdGEuZnJhbWUoYWJuZGF0YSksCiAgICAgICAgICAgICAgICAgZGF0YS5kaXN0cyA9IGRpc3QsIAogICAgICAgICAgICAgICAgIGdyb3VwLnZhciA9ICJmYXJtIiwKICAgICAgICAgICAgICAgICBjb3IudmFycyA9IGMoIkFSIiwgInBuZXVtUyIsICJmZW1hbGUiLCAibGl2ZGFtIiwgImVnZ3MiLCAid29ybUNvdW50IiwgImFnZSIsICJhZGciKSwKICAgICAgICAgICAgICAgICBjb21wdXRlLmZpeGVkID0gVFJVRSwgCiAgICAgICAgICAgICAgICAgbi5ncmlkID0gMTAwMCkKYGBgCgojIyBWaXN1YWxseSBpbnNwZWN0IHRoZSBtYXJnaW5hbCBwb3N0ZXJpb3IgZGlzdHJpYnV0aW9ucyBvZiB0aGUgcGFyYW1ldGVycwoKYGBge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD00fQojIGFscmFkeSBjb21wdXRlZCBtYXJnaW5hbHMKbG9hZChmaWxlID0gIm1hcmcuZ3JvdXBlZC5SRGF0YSIpCgpwYXIobWZyb3c9YygxLDQpLCBtYXI9YygyLDIsMS41LDEpKQpmb3IoaSBpbiAxOmxlbmd0aChtYXJnLmYuZ3JvdXBlZCRtYXJnaW5hbHMpKXsKCiMgZ2V0IHRoZSBtYXJnaW5hbCBmb3IgY3VycmVudCBub2RlLCB3aGljaCBpcyBhIG1hdHJpeCBbeCwgZih4KV0KICBjdXIubm9kZSA8LSBtYXJnLmYuZ3JvdXBlZCRtYXJnaW5hbHNbaV0KICBub20xIDwtIG5hbWVzKG1hcmcuZi5ncm91cGVkJG1hcmdpbmFscylbaV0KCiMgcGljayB0aGUgZmlyc3QgdmFsdWUgKGZvciBtb2RlbHMgd290aG91dCByYW5kb20gZWZmZWN0cykKICBjdXIubm9kZSA8LSBjdXIubm9kZVtbMV1dCiAgZm9yKGogaW4gMTpsZW5ndGgoY3VyLm5vZGUpICkgewogICAgbm9tMiA8LSBuYW1lcyhjdXIubm9kZSlbal0KICAgIGN1ci5wYXJhbSA8LSBjdXIubm9kZVtbal1dCiAgICBwbG90KGN1ci5wYXJhbSx0eXBlPSJsIixtYWluPXBhc3RlKG5vbTEsICI6Iiwgbm9tMiksIGNleD0wLjcpCiAgfQp9CmBgYAoKIyMgQ2FsY3VsYXRlIHRoZSBhcmVhIHVuZGVyIHRoZSBkZW5zaXR5IGN1cnZlCgpgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTR9CiNleHRyYWN0IG1hcmdpbmFscyAKbWFyZy5kZW5zIDwtIG1hcmcuZiRtYXJnaW5hbHNbWzFdXQpmb3IgKGkgaW4gMjpsZW5ndGgobWFyZy5mJG1hcmdpbmFscykpIHsKICBtYXJnLmRlbnMgPC0gYyhtYXJnLmRlbnMsIG1hcmcuZiRtYXJnaW5hbHNbW2ldXSkKfQoKIyBleHRyYWN0IG1hcmdpbmFscyBhZGp1c3RlZCBmb3IgZ3JvdXBlZCBkYXRhCm1hcmcuZGVucy5ncm91cGVkIDwtIG1hcmcuZi5ncm91cGVkJG1hcmdpbmFsc1tbMV1dCmZvciAoaSBpbiAyOmxlbmd0aChtYXJnLmYuZ3JvdXBlZCRtYXJnaW5hbHMpKSB7CiAgbWFyZy5kZW5zLmdyb3VwZWQgPC0gYyhtYXJnLmRlbnMuZ3JvdXBlZCwgbWFyZy5mLmdyb3VwZWQkbWFyZ2luYWxzW1tpXV0pCn0KCiMgY2FsY3VsYXRlIEFVQyAtLT4gc2hvdWxkIGJlIH4xCmF1YyA8LSByZXAoTkEsIGxlbmd0aChtYXJnLmRlbnMuZ3JvdXBlZCkpCm5hbWVzKGF1YykgPC0gbmFtZXMobWFyZy5kZW5zLmdyb3VwZWQpCmZvcihpIGluIDE6bGVuZ3RoKG1hcmcuZGVucy5ncm91cGVkKSkgewogIHRtcCA8LSBzcGxpbmUobWFyZy5kZW5zLmdyb3VwZWRbW2ldXSkKICBhdWNbaV0gPC0gc3VtKGRpZmYodG1wJHhbLWxlbmd0aCh0bXAkeCldKSp0bXAkeVstMV0pCn0KCmthYmxlKGNiaW5kKGF1YykpICU+JQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSAic3RyaXBlZCIsIGZ1bGxfd2lkdGggPSBGQUxTRSkKCnBhcihsYXM9MiwgbWFyPWMoOC4xLDQuMSw0LjEsMi4xKSkKYmFycGxvdChhdWMsIHlsYWI9IkFyZWEgdW5kZXIgRGVuc2l0eSIsIHlsaW09YygwLDEuMikpCmBgYAoKJm5ic3A7CgpIZXJlIHRoZSAqc2hhcGUqIG9mIHNvbWUgb2YgdGhlIHBhcmFtZXRlciBkaXN0cmlidXRpb24gbG9va3Mgd2VpcmQuIEluZGVlZCwgdGhlcmUgaXMgYSBkZWFsIG9mIGluZm9ybWF0aW9uIGJldHdlZW4gdGhlIHJhbmRvbSBzdHJhdGEuIEhvd2V2ZXIsIHRoZSAqQVVDKiBsb29rcyBnb29kLiBTbyB3ZSBjYW4gcHJvY2VlZCBnZXR0aW5nIHRoZSBlc3RpbWF0ZXMuCgombmJzcDsKCiMjIEdldCB0aGUgdGFibGUgb2YgcXVhbnRpbGVzIGZvciB0aGUgbWFyZ2luYWxzCgpBcyBvbmUgY2FuIHNlZSwgZXhjZXB0IGZvciBhIHByZWNpc2lvbiBwYXJhbWV0ZXIgKGBmZW1hbGV8Z3JvdXAucHJlY2lzaW9uYCkgdGhlIHF1YW50aWxlcyBzZWVtcyB0byBub3QgaGF2ZSB0b28gbXVjaCB3aWRlbi4gCgpgYGB7ciAsIGVjaG89RkFMU0V9CgptYXQgPC0gbWF0cml4KHJlcChOQSwgbGVuZ3RoKG1hcmcuZGVucy5ncm91cGVkKSozKSwgbmNvbD0zKQpyb3duYW1lcyhtYXQpIDwtIG5hbWVzKG1hcmcuZGVucy5ncm91cGVkKQpjb2xuYW1lcyhtYXQpIDwtIGMoIjIuNSUiLCAiNTAlIiwgIjk3LjUlIikKaWdub3JlLm1lIDwtIHVuaW9uKGdyZXAoIlxcKEludCIsIG5hbWVzKG1hcmcuZGVucy5ncm91cGVkKSksIGdyZXAoInByZWMiLCBuYW1lcyhtYXJnLmRlbnMuZ3JvdXBlZCkpKSAjIHRha2UgYXdheSBiYWNrZ3JvdW5kIGsgYW5kIHByZWNpc2lvbgpjb21tZW50IDwtIHJlcCgiIiwgbGVuZ3RoKG1hcmcuZGVucy5ncm91cGVkKSkKZm9yIChpIGluIDE6bGVuZ3RoKG1hcmcuZGVucy5ncm91cGVkKSkgewogIHRtcCA8LSBtYXJnLmRlbnMuZ3JvdXBlZFtbaV1dCiAgdG1wMiA8LSBjdW1zdW0odG1wWywyXSkvc3VtKHRtcFssMl0pCiAgbWF0W2ksIF0gPC1jKHRtcFt3aGljaCh0bXAyPjAuMDI1KVsxXS0xLDFdLCMjIC0xIGlzIHNvIHVzZSB2YWx1ZSBvbiB0aGUgbGVmdCBvZiB0aGUgMi41JQogICAgICAgICAgICAgICB0bXBbd2hpY2godG1wMj4wLjUpWzFdLDFdLAogICAgICAgICAgICAgICB0bXBbd2hpY2godG1wMj4wLjk3NSlbMV0sMV0pCiAgdmVjIDwtIG1hdFtpLF0KCiAgaWYoICEoaSVpbiVpZ25vcmUubWUpICYmICh2ZWNbMV08MCAmJiB2ZWNbM10+MCkpe2NvbW1lbnRbaV08LSJub3Qgc2lnLiBhdCA1JSJ9CgogICMjIHRydW5jYXRlIGZvciBwcmludGluZwogIG1hdFtpLF0gPC0gYXMubnVtZXJpYyhmb3JtYXRDKG1hdFtpLF0sZGlnaXRzPTMsZm9ybWF0PSJmIikpCn0KCm1hdDEgPC0gbWF0cml4KHJlcChOQSwgbGVuZ3RoKG1hcmcuZGVucykqMyksIG5jb2w9MykKcm93bmFtZXMobWF0MSkgPC0gbmFtZXMobWFyZy5kZW5zKQpjb2xuYW1lcyhtYXQxKSA8LSBjKCIyLjUlIiwgIjUwJSIsICI5Ny41JSIpCmlnbm9yZS5tZSA8LSB1bmlvbihncmVwKCJcXChJbnQiLCBuYW1lcyhtYXJnLmRlbnMpKSwgZ3JlcCgicHJlYyIsIG5hbWVzKG1hcmcuZGVucykpKSAjIHRha2UgYXdheSBiYWNrZ3JvdW5kIGsgYW5kIHByZWNpc2lvbgpjb21tZW50IDwtIHJlcCgiIiwgbGVuZ3RoKG1hcmcuZGVucykpCmZvciAoaSBpbiAxOmxlbmd0aChtYXJnLmRlbnMpKSB7CiAgdG1wIDwtIG1hcmcuZGVuc1tbaV1dCiAgdG1wMiA8LSBjdW1zdW0odG1wWywyXSkvc3VtKHRtcFssMl0pCiAgbWF0MVtpLCBdIDwtYyh0bXBbd2hpY2godG1wMj4wLjAyNSlbMV0tMSwxXSwjIyAtMSBpcyBzbyB1c2UgdmFsdWUgb24gdGhlIGxlZnQgb2YgdGhlIDIuNSUKICAgICAgICAgICAgICAgdG1wW3doaWNoKHRtcDI+MC41KVsxXSwxXSwKICAgICAgICAgICAgICAgdG1wW3doaWNoKHRtcDI+MC45NzUpWzFdLDFdKQogIHZlYyA8LSBtYXQxW2ksXQoKICBpZiggIShpJWluJWlnbm9yZS5tZSkgJiYgKHZlY1sxXTwwICYmIHZlY1szXT4wKSl7Y29tbWVudFtpXTwtIm5vdCBzaWcuIGF0IDUlIn0KCiAgIyMgdHJ1bmNhdGUgZm9yIHByaW50aW5nCiAgbWF0MVtpLF0gPC0gYXMubnVtZXJpYyhmb3JtYXRDKG1hdDFbaSxdLGRpZ2l0cz0zLGZvcm1hdD0iZiIpKQp9CiAKbWF0MSAlPiUKICBrYWJsZSgiaHRtbCIsIGFsaWduID0gJ2NsYycsIGNhcHRpb24gPSAnTm9uLWFkanVzdGVkIG1hcmdpbmFsIGRlbnNpdGllcycpICU+JQogICAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9ICJzdHJpcGVkIiwgZnVsbF93aWR0aCA9IEZBTFNFLCBwb3NpdGlvbiA9ICJmbG9hdF9sZWZ0IikKCm1hdCAlPiUKICBrYWJsZSgiaHRtbCIsIGFsaWduID0gJ2NsYycsIGNhcHRpb24gPSAnTWFyZ2luYWwgZGVuc2l0aWVzIGFkanVzdGVkIHdpdGggcmFuZG9tIGVmZmVjdHMgJykgJT4lCiAgICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gInN0cmlwZWQiLCBmdWxsX3dpZHRoID0gRkFMU0UsIHBvc2l0aW9uID0gImxlZnQiKQoKYGBgCgojIEhldXJpc3RpYyBzZWFyY2gKClRoZSBgbW9zdHByb2JhYmxlKClgIGZ1bmN0aW9uIGlzIGxpbWl0ZWQgdG8gMjAgdG8gMjUgbm9kZXMgKGlmIGNvbXB1dGVkIG9uIGEgY29tcHV0aW5nIGNsdXN0ZXIgZXZlbiB3aXRoIGEgbGltaXRlZCBudW1iZXIgb2YgcGFyZW50KS4gVGhlbiBmb3IgbGFyZ2VyIHByb2JsZW0gdGhpcyBhcHByb2FjaCBiZWNvbWVzIGludHJhY3RhYmxlIGZvcm0gYSBjb21wdXRhdGlvbmFsIHBvaW50IG9mIHZpZXcuIFRoZSBtYWluIGFkdmFudGFnZXMgb2YgdGhlIGBtb3N0cHJvYmFibGUoKWAgZnVuY3Rpb24gaXMgdGhhdCB0aGUgcmV0dXJuZWQgc3RydWN0dXJlIGlzIHRoZSBvbmUgdGhhdCBoYXMgdGhlIG1heGltYWwgcG9zc2libGUgc2NvcmUsIHRodXMgaXQgaXMgY2FsbGVkICoqZXhhY3QqKiBpbiB0aGF0IHNlbnNlIFtAa29pdmlzdG8yMDA0ZXhhY3RdLiBJdCBjb21wYXJlcyBhbGwgcG9zc2libGUgc3RydWN0dXJlIHRvIHNlbGVjdCB0aGUgb3B0aW1hbCBvbmUuIAoKVGhlIGhldXJpc3RpYyBzZWFyY2hlcyBhcmUgdGVjaG5pcXVlcyB0aGF0IHRlbmRzIHRvIHBlcmZvcm0gYSBncmVlZHkgb3B0aW1pemF0aW9uLiBUaGVuIHRoZXJlIGlzIG5vIGd1YXJhbnRlZXMgdG8gcmVhY2ggdGhlIG1heGltdW0gc2NvcmUuIEluIHRoaXMgY29udGV4dCBncmVlZHkgbWVhbnM6IHBlcmZvcm0gbG9jYWwgb3B0aW1pemF0aW9uIGFuZCBob3BpbmcgdG8gZ2V0IHRoZSBvdmVyYWxsIG1heGltdW0uCgpXZSB3aWxsIHVzZSBhIEhpbGwtQ2xpbWJlciBbQGtvcmIyMDEwYmF5ZXNpYW5dLiBXZSBuZWVkIHRvIHNldCB1cCB0aGUgbnVtYmVyIG9mIHNlYXJjaGVzIGFuZCB0aGUgbnVtYmVyIG9mIHN0ZXBzIHBlciBzZWFyY2hlcy4gVGhlIHN0YXJ0aW5nIHBvaW50IChoZXJlIHdlIGNob29zZSBhIHJhbmRvbSBEaXJlY3RlZCBBY3ljbGljIEdyYXBoIChEQUcpKS4gVGhlIHJlc3Qgb2YgcGFyYW1ldGVycyBpcyBlcXVpdmFsZW50IHRvIHRoZSBvbmVzIHVzZWQgaW4gYGJ1aWxkc2NvcmVjYWNoZSgpYCBmdW5jdGlvbi4KCmBgYHtyIGhldXJpc3RpYzEsIGVjaG89VFJVRSwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTR9CgphYm5kYXRhIDwtIGR0WywgLWRyb3BdCgphYm5kYXRhWywxOjVdIDwtIGFzLmRhdGEuZnJhbWUobGFwcGx5KGFibmRhdGFbLDE6NV0sIGZhY3RvcikpCmRpc3QgPC0gbGlzdChBUiA9ICJiaW5vbWlhbCIsIHBuZXVtUyA9ICJiaW5vbWlhbCIsIGZlbWFsZT0iYmlub21pYWwiLCAKICAgICAgICAgICAgIGxpdmRhbT0gImJpbm9taWFsIiwgZWdncyA9ICJiaW5vbWlhbCIsd29ybUNvdW50ID0gInBvaXNzb24iLAogICAgICAgICAgICAgYWdlPSAiZ2F1c3NpYW4iLCBhZGcgPSAiZ2F1c3NpYW4iKQoKI3NldCBtYXhpbXVtIG51bWJlciBvZiBwb3NzaWJsZSBwYXJlbnQgcGVyIG5vZGUKbWF4LnBhciA8LSA0CgojIGNvbXB1dGUgYSBjYWNoZSBvZiBzY29yZXMKbXljYWNoZSA8LSBidWlsZHNjb3JlY2FjaGUoZGF0YS5kZiA9IGFzLmRhdGEuZnJhbWUoYWJuZGF0YSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhLmRpc3RzID0gZGlzdCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG1heC5wYXJlbnRzID0gbWF4LnBhcikKCiMgc2V0IG51bWJlciBvZiBzZWFyY2hlcyBhbmQgbnVtYmVyIG9mIHN0ZXBzCm51bS5zZWFyY2hlcyA8LSAyMDAKbWF4LnN0ZXBzIDwtIDE1MAoKIyBIaWxsLWNsaW1iZXIKaGV1ci5yZXMgPC0gcXVpZXQoc2VhcmNoLmhldXJpc3RpYyhzY29yZS5jYWNoZSA9IG15Y2FjaGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjb3JlID0gIm1saWsiLAogICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhLmRpc3RzID0gZGlzdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbWF4LnBhcmVudHMgPSA0LAogICAgICAgICAgICAgICAgICAgICAgICAgICBzdGFydC5kYWcgPSAicmFuZG9tIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbnVtLnNlYXJjaGVzID0gbnVtLnNlYXJjaGVzLAogICAgICAgICAgICAgICAgICAgICAgICAgICBtYXguc3RlcHMgPSBtYXguc3RlcHMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlZWQgPSAzMjEzLAogICAgICAgICAgICAgICAgICAgICAgICAgICB2ZXJib3NlID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgYWxnbyA9ICJoYyIpKQoKIyBmb3IgY29tcGFyaXNvbiBsZXQgdXMgY29tcHV0ZSB0aGUgbWF4aW11bSBleGFjdCBzY29yZQpteWRhZyA8LSBxdWlldChtb3N0cHJvYmFibGUoc2NvcmUuY2FjaGUgPSBteWNhY2hlKSkKZmFibiA8LSBmaXRhYm4oZGFnLm0gPSBteWRhZywgZGF0YS5kZiA9IGFzLmRhdGEuZnJhbWUoYWJuZGF0YSksIGRhdGEuZGlzdHMgPSBkaXN0KQoKIyBwbG90IEhpbGwtQ2xpbWJlciBzY29yZXMKZGYuaGV1ciA8LSB1bmxpc3QoaGV1ci5yZXMkc2NvcmVzKQpwbG90KE5VTEwsbHR5PTEsIHhsYWI9IkluZGV4IG9mIGhldXJpc3RpYyBzZWFyY2giLHlsYWI9IkJOIHNjb3JlIiwgeWxpbSA9IGMobWF4KHVubGlzdChkZi5oZXVyKSksbWluKHVubGlzdChkZi5oZXVyKSkpLCB4bGltID0gYygwLG51bS5zZWFyY2hlcykpCmZvcihpIGluIDE6bnVtLnNlYXJjaGVzKXsKICBpZihzdW0oaT09b3JkZXIoZGYuaGV1cixkZWNyZWFzaW5nID0gVFJVRSlbMToxMF0pKXsKICAgIHBvaW50cyh4PWkseT1kZi5oZXVyW2ldLHR5cGU9InAiLHBjaD0xOSwgY29sPXJnYigwLDAsMSwgMC44KSxsd2QgPSAyKQogICAgfWVsc2V7CiAgICBwb2ludHMoeD1pLHk9ZGYuaGV1cltpXSx0eXBlPSJwIixwY2g9MTksIGNvbD1yZ2IoMCwwLDAsIDAuMykpCiAgfQp9CnBvaW50cyh4ID0gbWF4KHVubGlzdChkZi5oZXVyKSkseSA9IG1heCh1bmxpc3QoZGYuaGV1cikpLGNvbD0icmVkIixwY2g9MTkpCmFibGluZShoID0gZmFibiRtbGlrLCBjb2w9InJlZCIsbHR5ID0gMykKCmBgYApBYm92ZSB3ZSBwbG90IHRoZSBtYXhpbXVtIHNjb3JlcyBhZnRlciAxNTAgc3RlcHMgb3V0IG9mIDIwMCBkaWZmZXJlbnQgc2VhcmNoZXMgb2YgYSBIaWxsLUNsaW1iZXIuIFRoZSBibHVlcyBkb3RlcyBhcmUgdGhlIDEwIGJlc3Qgc2VhcmNoZXMuIFRoZSByZWQgZGFzaGVkIGxpbmUgaXMgdGhlIG1heGltdW0gcG9zc2libGUgc2NvcmUuIAoKYGBge3IgaGV1cmlzdGljMiwgZWNobz1UUlVFLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9NH0KIyBsZXQgdXMgcGxvdCB0aGUgZXZvbHV0aW9uIG9mIHNjb3JlcyBkdXJpbmcgb3B0aW1pemF0aW9uCkxvbmcgPC0gKGhldXIucmVzJGRldGFpbGVkLnNjb3JlKQpMb25nLmFyciA8LSBhcnJheSh1bmxpc3QoTG9uZyksIGRpbSA9IGMobnJvdyhMb25nW1sxXV0pLCBuY29sKExvbmdbWzFdXSksIGxlbmd0aChMb25nKSkpCnBsb3QoTlVMTCxsdHk9MSwgeGxhYj0iTnVtYmVyIG9mIFN0ZXBzIix5bGFiPSJCTiBzY29yZSIsIHlsaW0gPSBjKG1heCh1bmxpc3QoZGYuaGV1cikpLG1pbih1bmxpc3QoZGYuaGV1cikpKSwgeGxpbSA9IGMoMCxtYXguc3RlcHMpKQpmb3IoaSBpbiAxOm51bS5zZWFyY2hlcyl7CiAgaWYoc3VtKGk9PW9yZGVyKHVubGlzdChoZXVyLnJlcyRzY29yZXMpLGRlY3JlYXNpbmcgPSBUUlVFKVsxOjEwXSkpewogICAgcG9pbnRzKHg9MToobWF4LnN0ZXBzLTEpLHk9TG9uZy5hcnJbMSwsaV0sdHlwZT0ibCIsbHR5PTEsIGNvbD1yZ2IoMCwwLDEsIDAuOCksbHdkID0gMikKICAgIH1lbHNlewogICAgcG9pbnRzKHg9MToobWF4LnN0ZXBzLTEpLHk9TG9uZy5hcnJbMSwsaV0sdHlwZT0ibCIsbHR5PTEsIGNvbD1yZ2IoMCwwLDAsIDAuMjUpKQogIH0KfQpsaW5lcyh4PTE6KG1heC5zdGVwcy0xKSx5PUxvbmcuYXJyWzEsLHdoaWNoLm1heCh1bmxpc3QoaGV1ci5yZXMkc2NvcmVzKSldLHR5cGU9ImwiLGNvbD0icmVkIixsd2Q9MykKYWJsaW5lKGggPSBmYWJuJG1saWssIGNvbD0icmVkIixsdHkgPSAzKQoKYGBgCkFib3ZlIHdlIHBsb3QgdGhlIHNjb3JlcyBpbiBmdW5jdGlvbiBvZiB0aGUgc3RlcHMgZm9yIHRoZSAyMDAgc2VhcmNoZXMuIFRoZSBibHVlIGxpbmVzIGFyZSB0aGUgMTAgYmVzdCBhbmQgdGhlIHJlZCBvbmUgaXMgKip0aGUqKiBvbmUgdGhhdCBoYXMgdGhlIGhpZ2hlc3Qgc2NvcmUuIEFzIG9uZSBjYW4gc2VlLCBpbiBzZWFyY2hlcyB0aGF0IHdvcmsgd2VsbCB0aGUgb3B0aW1pemF0aW9uIGlzIGFscmVhZHkgZ29vZCBhZnRlciA2MCBzdGVwcy4gVGhpcyBwbG90IGlzIGEgZGlhZ25vc3RpYyBwbG90IHVzZWQgdG8gc2V0IHVwIHRoZSBudW1iZXIgb2YgcmVxdWlyZWQgc3RlcHMuIAoKIyBNQ01DIG92ZXIgdGhlIHN0cnVjdHVyZXMKClVzdWFsbHksIHRoZSBvdXRwdXQgb2YgYSBCYXllc2lhbiBuZXR3b3JrIGFuYWx5c2lzIG9mIGEgZGF0YXNldCBlbmRzLXVwIHdpdGggYSBzaW5nbGUgd2VsbCBhZGp1c3RlZCBEQUcuIEZyb20gdGhlIHJlc2VhcmNoZXIgcG9pbnQgb2YgdmlldyB0aGlzIGNvdWxkIGJlIGZydXN0cmF0aW5nLiBJbmRlZWQsIHRoZSBtb2RlbCBpcyB0aGUgb25lIHRoYXQgaXMgYmVzdCBzdXBwb3J0ZWQgYnkgdGhlIGRhdGEgYnV0IHRoZSB1bmNlcnRhaW50eSBxdWFudGlmaWNhdGlvbiBpcyBtaXNzaW5nLiBDbGFzc2ljYWxseSBpbiBlcGlkZW1pb2xvZ3ksIHJlc2VhcmNoZXJzIGFyZSB1c2VkIHRvIGV4cHJlc3MgcG9pbnQgZXN0aW1hdGUgd2l0aCBhbiB1bmNlcnRhaW50eSBtZWFzdXJlLiBBbiBhcmMgaW4gYSBCYXllc2lhbiBuZXR3b3JrIGlzIGEgcG9pbnQgZXN0aW1hdGUsIHdlIHdpbGwgc2VlIGhvdyB0byBwZXJmb3JtIG1vZGVsIGF2ZXJhZ2luZy4gVGhlICpsaW5rIHN0cmVuZ3RoKiBtZWFzdXJlIGlzIGRlc2lnbmVkIHRvIGFjY291bnQgZm9yIHRoYXQuIEFuIG1vcmUgbmF0dXJhbCBhbHRlcm5hdGl2ZSBpcyB0byBwZXJmb3JtIE1DTUMgb3ZlciBzdHJ1Y3R1cmVzIFtAZnJpZWRtYW4yMDAzYmVpbmddLiAKCldlIHVzZSB0aGUgYG1jbWNhYm4oKWAgZnVuY3Rpb24gb24gdGhlIGNhY2hlIG9mIHByZS1jb21wdXRlZCBuZXR3b3JrcyBzY29yZXMuIE9uZSBuZWVkcyB0byBkZWZpbmUgdGhlIHR5cGUgb2Ygc2NvcmUgdXNlZCAoaGVyZSBpcyB0aGUgbWFyZ2luYWwgbGlrZWxpaG9vZCBgbWxpa2ApLiBUaGUgbWF4aW11bSBvZiBudW1iZXIgb2YgcGFyZW50cyBwZXIgbm9kZSAoc2FtZSBhcyB0aGUgb25lIHVzZWQgaW4gYGJ1aWxkc2NvcmVjYWNoZSgpYCkuIFRoZSBNQ01DIGxlYXJuaW5nIHNjaGVtZSwgZGVmaW5lZCBhczogbnVtYmVyIG9mIE1DTUMgc2FtcGxlcywgbnVtYmVyIG9mIHRoaW5uZWQgc2FtcGxlZCAodG8gYXZvaWQgYXV0b2NvcnJlbGF0aW9uKSBhbmQgdGhlIGxlbmd0aCBvZiB0aGUgYnVybi1pbiBwaGFzZS4gUG9zc2libHkgYSBzdGFydGluZyBEQUcgYW5kIGEgc3RydWN0dXJhbCBwcmlvci4gV2UgYWxzbyBuZWVkIHRvIHNlbGVjdCB0aGUgcmVsYXRpdmUgcHJvYmFiaWxpdHkgb2YgcGVyZm9ybWluZyByYWRpY2FsIG1vdmVzIChzaHVmZmxpbmcpLiBJbmRlZWQsIGEgbmFpdmUgTUNNQyBhcHByb2FjaCBpcyBrbm93biB0byBnZXQgdmVyeSBlYXNpbHkgc3R1Y2sgaW4gbG9jYWwgbWF4aW11bSAoZm9yIG1vcmUgZGV0YWlscyBzZWU6IFtAZ3J6ZWdvcmN6eWsyMDA4aW1wcm92aW5nO0BzdTIwMTZpbXByb3ZpbmddKS4gCgpgYGB7cixldmFsPUZBTFNFLCBlY2hvPVRSVUV9Cm1jbWMub3V0IDwtIG1jbWNhYm4oc2NvcmUuY2FjaGUgPSBteWNhY2hlLAogICAgICAgICAgICAgICAgICBzY29yZSA9ICJtbGlrIiwKICAgICAgICAgICAgICAgICAgZGF0YS5kaXN0cyA9IGRpc3QsCiAgICAgICAgICAgICAgICAgIG1heC5wYXJlbnRzID0gNCwKICAgICAgICAgICAgICAgICAgbWNtYy5zY2hlbWUgPSBjKDEwMDAsOSw1MDApLAogICAgICAgICAgICAgICAgICBzZWVkID0gMzIxLAogICAgICAgICAgICAgICAgICB2ZXJib3NlID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgIHN0YXJ0LmRhZyA9ICJyYW5kb20iLAogICAgICAgICAgICAgICAgICBwcm9iLnJldiA9IDAuMDcsCiAgICAgICAgICAgICAgICAgIHByb2IubWJyID0gMC4wNywKICAgICAgICAgICAgICAgICAgcHJpb3IuY2hvaWNlID0gMSkKYGBgCgpUaGlzIGlzIGFnYWluIGNvbXB1dGF0aW9uYWxseSBjb21wbGV4IQoKSGVyIGlzIGEgcGxvdCBvZiB0aGUgTUNNQyBzYW1wbGVzLiBPbmUgY2FuIHNlZSB0aGUgc2NvcmVzIG9mIHRoZSBzdHJ1Y3R1cmVzIG9uIHRoZSB5LWF4aXMgaW4gZnVuY3Rpb24gb2YgdGhlIGluZGV4IHN0ZXBzLiBUaGUgZG90cyBhcmUgdGhlIHJhZGljYWwgbW92ZXMuIE9uZSB0aGUgcmlnaHQgc2lkZSBhIGhpc3RvZ3JhbSBzaG93cyB0aGUgbnVtYmVyIG9mIHN0cnVjdHVyZXMgd2l0aCBhIGdpdmVuIHNjb3JlLiBBcyBvbmUgY2FuIHNlZSB0aGUgaGlzdG9ncmFtIGlzIHZlcnkgcGVha2VkIG9uIHRoZSBtYXhpbXVtIHBvc3NpYmxlIHNjb3JlLiAKCmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9NH0KbG9hZCgibWNtYy5SZGF0YSIpCnBsb3QobWNtYy5vdXQpCmBgYAoKT25lIGNhbiBhbHNvIGRpc3BsYXkgdGhlIGN1bXVsYXRpdmUgbWF4aW11bSBzY29yZSAodXNlZCBmb3IgbmV0d29yayBzY29yZSBvcHRpbWl6YXRpb24pLgoKYGBge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD00fQpwbG90KG1jbWMub3V0LG1heC5zY29yZT1UUlVFKQpgYGAKCkJ1dCB0aGUgbWFqb3IgYWR2YW50YWdlIG9mIHRoaXMgbWV0aG9kIGlzIHRoZSBwb3NzaWJpbGl0eSBvZiBxdWVyeWluZyB0aGUgTUNNQyBzYW1wbGUgdXNpbmcgYSBmb3JtdWxhIHN0YXRlbWVudC4KCmBgYHtyLCBlY2hvPVRSVUUsIGV2YWw9VFJVRX0KIyBhdmVyYWdlIGluZGl2aWR1YWwgYXJjIHN1cHBvcnQKcXVlcnkobWNtY2FibiA9IG1jbWMub3V0KQoKIyBwcm9iYWJpbGl0eSB0aGF0IHdvcm0gY291bnQgYmVpbmcgbGlua2VkIHRvIGFnZSBidXQgbm90IHRvIGZlbWFsZSBkaXJlY3RseQpxdWVyeShtY21jYWJuID0gbWNtYy5vdXQsZm9ybXVsYSA9IH53b3JtQ291bnR8YWdlLXdvcm1Db3VudHxmZW1hbGUpCgojIHByb2JhYmlsaXR5IHRoYXQgd29ybSBjb3VudCBiZWluZyBkaXJlY3RseSBsaW5rZWQgdG8gYWdlIGFuZCBhZGcgYW5kIHRoYXQgYWRnIGlzIGxpbmsgdG8gYWdlICh1bmRpcmVjdGVkKQpxdWVyeShtY21jYWJuID0gbWNtYy5vdXQsZm9ybXVsYSA9IH53b3JtQ291bnR8YWdlICsgd29ybUNvdW50fGFkZyArIGFnZXxhZGcpKwogIHF1ZXJ5KG1jbWNhYm4gPSBtY21jLm91dCxmb3JtdWxhID0gfndvcm1Db3VudHxhZ2UgKyB3b3JtQ291bnR8YWRnICsgYWRnfGFnZSkKYGBgCiMgUmVmZXJlbmNlcw==