Introduction
This vignette contains examples generated with ConfoundingExplorer.
The package can be installed from GitHub:
install.packages("remotes")
remotes::install_github("csoneson/ConfoundingExplorer")
After installing the package, an app can be launched by typing
library(ConfoundingExplorer)
ConfoundingExplorer()
in the R console.
Overview of the interface

The ConfoundingExplorer interface consists of two parts - a sidebar to the
left where the user can change the settings and the experimental design, and
a collection of panels displaying various aspects of the generated data and
results from statistical tests. The main purpose of the app is to evaluate the
ability to correctly detect differentially expressed features between two
conditions, when the samples may be collected in different batches.
A detailed description of how the data is generated can be found by clicking
on the Data description button in the bottom of the sidebar, but briefly,
a base matrix with 1,000 features (rows) and the number of samples (columns)
equal to the sum of the numbers in the table in the top of the sidebar is
first generated by random draws from a normal distribution with mean 10 and
standard deviation 2. Next, condition effects and batch effects are added
or subtracted from the samples from a given condition or batch. The strength of
the condition and batch signals can be modified using the sliders in the
sidebar, and the number of samples in each condition and batch can be changed
by modifying the table in the top-left corner.
The Analysis approach section of the sidebar determines how the generated
data will be analysed. In each case, a linear model will be used to compare the
values for each feature between the two conditions, but depending on the
choice, the batch effect will be accounted for in different ways. Again, more
details can be obtained by clicking on the Data description button.
The first row of panels in the main window illustrate the results of the
statistical test. The left panel shows the number of features in each
category (affected by condition effect and/or by batch effect), and the
features that are detected as significantly differentially expressed between
the conditions are colored in red. The right panel shows a histogram of all the
1,000 p-values. If there is no signal in the data (no truly differentially
expressed features) we expect this histogram to show a uniform distribution.
If there are some truly differential features, we expect additionally a peak
close to p=0. A peak close to p=1 often indicates a problem with the data, e.g.,
a factor that affects the data but that is not accounted for in the analysis.
The next panel (Summary) provides a summary of the statistical test results.
More precisely, it shows the TPR (true positive rate, or power - the fraction
of the features that are truly affected by the condition that are detected
as significantly differential), the FDP (false discovery proportion - the
fraction of the features detected as significant that are false positives),
and the FPR (false positive rate - the fraction of the truly non-differential
features that are nevertheless found to be significant).
The Data heatmap shows the simulated data in the form of a heatmap. It also
indicates the condition and batch assignment of the samples (here shown in the
rows), and the true designation of the features (batch- and/or
condition-affected) as well as whether they are detected as significantly
differential between the two conditions.
Finally, the last row of panels show examples from each of the four
categories of features (same categories as in the top-right panel). A new set
of examples can be obtained by clicking on the New selection button.
Non-confounded setup
First, we consider the setup where there is no confounding between the condition (group) and the batch.
In other words, each condition is equally represented in each of the batches.
We will assume that 25% of the features are truly different between the two conditions, and that 50% of the features are affected by the batch.
The two sets of genes overlap partly.
Thus, some features that are truly different between conditions are also affected by the batch.
Don’t adjust for the batch effect
We first consider the results if we don’t adjust for the batch effect at all, and just compare the two conditions.
Since each condition contains samples from both batches, the within-condition variance estimate will also incorporate the differences induced by the batch effect, and thus be artificially inflated compared to the ‘true’ (within-batch) within-condition variance.
The effect on the results of the test comparing the two conditions is that we will have lower power to detect true differences (due to the overestimated variance).
app <- ConfoundingExplorer(
sampleSizes = matrix(rep(5, 4), nrow = 2,
dimnames = list(c("group1", "group2"),
c("batch1", "batch2"))),
fracVarCond = 0.25,
fracVarBatch = 0.5,
condEffectSize = 4,
batchEffectSize = 4,
analysisApproach = "dontAdjust"
)

Increase the size of the batch effect, don’t adjust
To see the effect described above even clearer, let’s increase the size of the batch effect.
This simulates a situation where, for example, five replicates of each condition were obtained by each of two technologies, or using completely different protocols.
Now we see the loss of power much more strikingly, and we notice a strangely shaped p-value histogram.
app <- ConfoundingExplorer(
sampleSizes = matrix(rep(5, 4), nrow = 2,
dimnames = list(c("group1", "group2"),
c("batch1", "batch2"))),
fracVarCond = 0.25,
fracVarBatch = 0.5,
condEffectSize = 4,
batchEffectSize = 10,
analysisApproach = "dontAdjust"
)

Adjust for the batch effect by including it as a covariate
The recommended way of adjusting for batch effects in statistical modeling is to include them as a covariate (an ‘additional predictor variable’) in the model.
Here we follow this approach, and thus model the observed values as a linear combination of the estimated batch effect and the estimated condition effect.
As before, the p-values correspond to the test of the condition effect (but now we’re accounting for the batch effect).
We note that by including the extra predictor corresponding to the batch effect, we can explain some of the between-sample variance and thus increase the power to detect differences.
app <- ConfoundingExplorer(
sampleSizes = matrix(rep(5, 4), nrow = 2,
dimnames = list(c("group1", "group2"),
c("batch1", "batch2"))),
fracVarCond = 0.25,
fracVarBatch = 0.5,
condEffectSize = 4,
batchEffectSize = 4,
analysisApproach = "inclBatch"
)

Fully confounded setup
Next, we consider a fully confounded setup.
Here, the 10 replicates of each condition were all acquired in the same batch, and only samples from one condition were included in each batch.
Don’t adjust for the batch effect
If we do not adjust for the batch effect in this case, both the batch and condition effects are interpreted as the condition effect, which leads to a high false positive rate.
In other words, many features are interpreted as being different between the two conditions, whereas the difference in reality is caused by the batch effect, which is completely confounded with the condition.
In addition, some features that are truly differential between the two conditions are no longer detected, since the difference is counteracted by the batch effect.
app <- ConfoundingExplorer(
sampleSizes = matrix(c(10, 0, 0, 10), nrow = 2,
dimnames = list(c("group1", "group2"),
c("batch1", "batch2"))),
fracVarCond = 0.25,
fracVarBatch = 0.5,
condEffectSize = 4,
batchEffectSize = 4,
analysisApproach = "dontAdjust"
)

Attempt to adjust for the batch effect
In the non-confounded situation above, we could adjust for the batch effect by including it as a covariate in the model.
When the batch and condition factors are fully confounded, however, this is not possible, since it would mean including two perfectly correlated predictors in the model.
app <- ConfoundingExplorer(
sampleSizes = matrix(c(10, 0, 0, 10), nrow = 2,
dimnames = list(c("group1", "group2"),
c("batch1", "batch2"))),
fracVarCond = 0.25,
fracVarBatch = 0.5,
condEffectSize = 4,
batchEffectSize = 4,
analysisApproach = "inclBatch"
)

Partly confounded setup
Finally, we consider a partly confounded setup. Here, each batch contains 9 samples of one condition and 1 sample of the other condition.
Adjust for the batch effect by including it as a covariate
As in the non-confounded case, we attempt to adjust for the batch effect by including it as a covariate in the model.
While it is now statistically feasible (as opposed to the fully confounded case), we notice that the power to detect true differences between the conditions is lower than in the non-confounded case.
app <- ConfoundingExplorer(
sampleSizes = matrix(c(9, 1, 1, 9), nrow = 2,
dimnames = list(c("group1", "group2"),
c("batch1", "batch2"))),
fracVarCond = 0.25,
fracVarBatch = 0.5,
condEffectSize = 4,
batchEffectSize = 4,
analysisApproach = "inclBatch"
)

Remove the batch effect in advance
Another approach that is sometimes used, especially when the data is going to be used for exploratory analysis rather than statistical modeling, is to estimate the batch effect in advance, and subtract it from the observed values.
Here we illustrate this approach for the partly confounded setup.
We notice that almost all the true signal is removed by this procedure, since we do not inform the batch effect correction step of the condition effect, and thus any signal that could potentially be explained by the batch variable will be removed.
app <- ConfoundingExplorer(
sampleSizes = matrix(c(9, 1, 1, 9), nrow = 2,
dimnames = list(c("group1", "group2"),
c("batch1", "batch2"))),
fracVarCond = 0.25,
fracVarBatch = 0.5,
condEffectSize = 4,
batchEffectSize = 4,
analysisApproach = "removeBatch"
)

Remove batch effect in advance, accounting for the condition
As an alternative to the approach outlined in the previous section, we could inform the batch effect adjustment step of the condition factor, and instruct it to not remove any effect that could be explained by the latter.
Since the batch and condition factors are highly similar, the effect of this is that very little of the batch effect is in fact eliminated.
app <- ConfoundingExplorer(
sampleSizes = matrix(c(9, 1, 1, 9), nrow = 2,
dimnames = list(c("group1", "group2"),
c("batch1", "batch2"))),
fracVarCond = 0.25,
fracVarBatch = 0.5,
condEffectSize = 4,
batchEffectSize = 4,
analysisApproach = "removeBatchAccCond"
)

Conclusion
The examples above illustrate the complications that arise when the condition of interest is (partly or fully) confounded with a batch effect or another source of unwanted variation affecting the data.
It is important to note that in a fully confounded experimental setup, statistical modeling can never ‘rescue’ the experiment, and there is no reliable way of distinguishing the effect of the batch and the condition, respectively.
In a non-confounded experiment, even if there is a batch effect, it can typically be accounted for in the statistical modeling.
Session info
sessionInfo()
# R version 4.3.2 (2023-10-31)
# Platform: aarch64-apple-darwin20 (64-bit)
# Running under: macOS Sonoma 14.3
#
# Matrix products: default
# BLAS: /Library/Frameworks/R.framework/Versions/4.3-arm64/Resources/lib/libRblas.0.dylib
# LAPACK: /Library/Frameworks/R.framework/Versions/4.3-arm64/Resources/lib/libRlapack.dylib; LAPACK version 3.11.0
#
# locale:
# [1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
#
# time zone: Europe/Zurich
# tzcode source: internal
#
# attached base packages:
# [1] stats graphics grDevices utils datasets methods base
#
# other attached packages:
# [1] ConfoundingExplorer_0.5.0 BiocStyle_2.30.0
#
# loaded via a namespace (and not attached):
# [1] bitops_1.0-7 gridExtra_2.3
# [3] rlang_1.1.3 magrittr_2.0.3
# [5] shinydashboard_0.7.2 clue_0.3-65
# [7] GetoptLong_1.0.5 matrixStats_1.2.0
# [9] compiler_4.3.2 mgcv_1.9-1
# [11] png_0.1-8 vctrs_0.6.5
# [13] reshape2_1.4.4 stringr_1.5.1
# [15] pkgconfig_2.0.3 shape_1.4.6
# [17] crayon_1.5.2 fastmap_1.1.1
# [19] XVector_0.42.0 ellipsis_0.3.2
# [21] fontawesome_0.5.2 utf8_1.2.4
# [23] promises_1.2.1 rmarkdown_2.25
# [25] shinyAce_0.4.2 UpSetR_1.4.0
# [27] purrr_1.0.2 xfun_0.41
# [29] zlibbioc_1.48.0 cachem_1.0.8
# [31] GenomeInfoDb_1.38.5 jsonlite_1.8.8
# [33] highr_0.10 later_1.3.2
# [35] DelayedArray_0.28.0 parallel_4.3.2
# [37] cluster_2.1.6 R6_2.5.1
# [39] bslib_0.6.1 stringi_1.8.3
# [41] RColorBrewer_1.1-3 limma_3.58.1
# [43] GenomicRanges_1.54.1 jquerylib_0.1.4
# [45] shinyMatrix_0.6.0 Rcpp_1.0.12
# [47] bookdown_0.37 SummarizedExperiment_1.32.0
# [49] iterators_1.0.14 knitr_1.45
# [51] IRanges_2.36.0 splines_4.3.2
# [53] igraph_1.6.0 httpuv_1.6.14
# [55] Matrix_1.6-5 tidyselect_1.2.0
# [57] rstudioapi_0.15.0 abind_1.4-5
# [59] yaml_2.3.8 miniUI_0.1.1.1
# [61] doParallel_1.0.17 codetools_0.2-19
# [63] lattice_0.22-5 tibble_3.2.1
# [65] plyr_1.8.9 shiny_1.8.0
# [67] Biobase_2.62.0 ROCR_1.0-11
# [69] evaluate_0.23 circlize_0.4.15
# [71] pillar_1.9.0 BiocManager_1.30.22
# [73] MatrixGenerics_1.14.0 DT_0.31
# [75] foreach_1.5.2 stats4_4.3.2
# [77] shinyjs_2.1.0 generics_0.1.3
# [79] iSEE_2.14.0 RCurl_1.98-1.14
# [81] S4Vectors_0.40.2 ggplot2_3.4.4
# [83] munsell_0.5.0 scales_1.3.0
# [85] xtable_1.8-4 glue_1.7.0
# [87] tools_4.3.2 colourpicker_1.3.0
# [89] cowplot_1.1.3 grid_4.3.2
# [91] tidyr_1.3.1 iCOBRA_1.30.0
# [93] shinyBS_0.61.1 colorspace_2.1-0
# [95] SingleCellExperiment_1.24.0 nlme_3.1-164
# [97] GenomeInfoDbData_1.2.11 vipor_0.4.7
# [99] cli_3.6.2 fansi_1.0.6
# [101] viridisLite_0.4.2 S4Arrays_1.2.0
# [103] ComplexHeatmap_2.18.0 dplyr_1.1.4
# [105] gtable_0.3.4 rintrojs_0.3.4
# [107] sass_0.4.8 digest_0.6.34
# [109] BiocGenerics_0.48.1 ggrepel_0.9.5
# [111] SparseArray_1.2.3 rjson_0.2.21
# [113] htmlwidgets_1.6.4 memoise_2.0.1
# [115] htmltools_0.5.7 lifecycle_1.0.4
# [117] shinyWidgets_0.8.1 GlobalOptions_0.1.2
# [119] statmod_1.5.0 mime_0.12
LS0tCnRpdGxlOiA+CiAgRXhwZXJpbWVudGFsIERlc2lnbiAtIGV4ZXJjaXNlcwpzdWJ0aXRsZTogPgogIFNjaWVudGlmaWMgSW5xdWlyeSAyMDI0CmF1dGhvcjoKLSBuYW1lOiA8YSBocmVmPSJodHRwczovL2Nzb25lc29uLmdpdGh1Yi5pbyI+Q2hhcmxvdHRlIFNvbmVzb24gKGNoYXJsb3R0ZS5zb25lc29uQGZtaS5jaCk8L2E+CmRhdGU6ICIyMDI0LTAzLTA4IgpvdXRwdXQ6IAogIGJvb2tkb3duOjpodG1sX2RvY3VtZW50MjoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICB0aGVtZTogY29zbW8KICAgIGNvZGVfZm9sZGluZzogc2hvdwogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgc2VsZl9jb250YWluZWQ6IHRydWUKZWRpdG9yX29wdGlvbnM6IAogIGNodW5rX291dHB1dF90eXBlOiBjb25zb2xlCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGUgPSBGQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KAogIGNvbGxhcHNlID0gVFJVRSwKICBjb21tZW50ID0gIiMiLAogIGVycm9yID0gRkFMU0UsCiAgd2FybmluZyA9IEZBTFNFLAogIG1lc3NhZ2UgPSBGQUxTRQopCgpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMoewogICAgcmVxdWlyZShCaW9jU3R5bGUpCiAgICByZXF1aXJlKENvbmZvdW5kaW5nRXhwbG9yZXIpCn0pCmh0bWx0b29sczo6dGFnTGlzdChybWFya2Rvd246Omh0bWxfZGVwZW5kZW5jeV9mb250X2F3ZXNvbWUoKSkKYGBgCgpgYGB7ciwgZXZhbD0hZXhpc3RzKCJTQ1JFRU5TSE9UIiksIGluY2x1ZGU9RkFMU0V9ClNDUkVFTlNIT1QgPC0gZnVuY3Rpb24oeCwgLi4uKSBrbml0cjo6aW5jbHVkZV9ncmFwaGljcyh4KQpgYGAKCiMgSW50cm9kdWN0aW9uCgpUaGlzIHZpZ25ldHRlIGNvbnRhaW5zIGV4YW1wbGVzIGdlbmVyYXRlZCB3aXRoIFtgQ29uZm91bmRpbmdFeHBsb3JlcmBdKGh0dHBzOi8vZ2l0aHViLmNvbS9jc29uZXNvbi9Db25mb3VuZGluZ0V4cGxvcmVyKS4gCgpUaGUgcGFja2FnZSBjYW4gYmUgaW5zdGFsbGVkIGZyb20gR2l0SHViOgoKYGBge3IsIGV2YWw9RkFMU0V9Cmluc3RhbGwucGFja2FnZXMoInJlbW90ZXMiKQpyZW1vdGVzOjppbnN0YWxsX2dpdGh1YigiY3NvbmVzb24vQ29uZm91bmRpbmdFeHBsb3JlciIpCmBgYAoKQWZ0ZXIgaW5zdGFsbGluZyB0aGUgcGFja2FnZSwgYW4gYXBwIGNhbiBiZSBsYXVuY2hlZCBieSB0eXBpbmcgCgpgYGB7ciwgZXZhbD1GQUxTRX0KbGlicmFyeShDb25mb3VuZGluZ0V4cGxvcmVyKQpDb25mb3VuZGluZ0V4cGxvcmVyKCkKYGBgCgppbiB0aGUgUiBjb25zb2xlLgoKIyBPdmVydmlldyBvZiB0aGUgaW50ZXJmYWNlCgpgYGB7ciwgZWNobz1GQUxTRX0KYXBwIDwtIENvbmZvdW5kaW5nRXhwbG9yZXIoKQpgYGAKCmBgYHtyLCBlY2hvPUZBTFNFfQpTQ1JFRU5TSE9UKCJzY3JlZW5zaG90cy9jb25mb3VuZGluZ2V4cGxvcmVyLWRlZmF1bHQtc2V0dGluZ3MucG5nIiwKICAgICAgICAgICB2d2lkdGggPSAxMzAwKQpgYGAKClRoZSBgQ29uZm91bmRpbmdFeHBsb3JlcmAgaW50ZXJmYWNlIGNvbnNpc3RzIG9mIHR3byBwYXJ0cyAtIGEgc2lkZWJhciB0byB0aGUgCmxlZnQgd2hlcmUgdGhlIHVzZXIgY2FuIGNoYW5nZSB0aGUgc2V0dGluZ3MgYW5kIHRoZSBleHBlcmltZW50YWwgZGVzaWduLCBhbmQgCmEgY29sbGVjdGlvbiBvZiBwYW5lbHMgZGlzcGxheWluZyB2YXJpb3VzIGFzcGVjdHMgb2YgdGhlIGdlbmVyYXRlZCBkYXRhIGFuZCAKcmVzdWx0cyBmcm9tIHN0YXRpc3RpY2FsIHRlc3RzLiBUaGUgbWFpbiBwdXJwb3NlIG9mIHRoZSBhcHAgaXMgdG8gZXZhbHVhdGUgdGhlCmFiaWxpdHkgdG8gY29ycmVjdGx5IGRldGVjdCBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZmVhdHVyZXMgYmV0d2VlbiB0d28gCmNvbmRpdGlvbnMsIHdoZW4gdGhlIHNhbXBsZXMgbWF5IGJlIGNvbGxlY3RlZCBpbiBkaWZmZXJlbnQgYmF0Y2hlcy4gCgpBIGRldGFpbGVkIGRlc2NyaXB0aW9uIG9mIGhvdyB0aGUgZGF0YSBpcyBnZW5lcmF0ZWQgY2FuIGJlIGZvdW5kIGJ5IGNsaWNraW5nIApvbiB0aGUgYERhdGEgZGVzY3JpcHRpb25gIGJ1dHRvbiBpbiB0aGUgYm90dG9tIG9mIHRoZSBzaWRlYmFyLCBidXQgYnJpZWZseSwgCmEgYmFzZSBtYXRyaXggd2l0aCAxLDAwMCBmZWF0dXJlcyAocm93cykgYW5kIHRoZSBudW1iZXIgb2Ygc2FtcGxlcyAoY29sdW1ucykgCmVxdWFsIHRvIHRoZSBzdW0gb2YgdGhlIG51bWJlcnMgaW4gdGhlIHRhYmxlIGluIHRoZSB0b3Agb2YgdGhlIHNpZGViYXIgaXMgCmZpcnN0IGdlbmVyYXRlZCBieSByYW5kb20gZHJhd3MgZnJvbSBhIG5vcm1hbCBkaXN0cmlidXRpb24gd2l0aCBtZWFuIDEwIGFuZCAKc3RhbmRhcmQgZGV2aWF0aW9uIDIuIE5leHQsIGNvbmRpdGlvbiBlZmZlY3RzIGFuZCBiYXRjaCBlZmZlY3RzIGFyZSBhZGRlZCAKb3Igc3VidHJhY3RlZCBmcm9tIHRoZSBzYW1wbGVzIGZyb20gYSBnaXZlbiBjb25kaXRpb24gb3IgYmF0Y2guIFRoZSBzdHJlbmd0aCBvZiAKdGhlIGNvbmRpdGlvbiBhbmQgYmF0Y2ggc2lnbmFscyBjYW4gYmUgbW9kaWZpZWQgdXNpbmcgdGhlIHNsaWRlcnMgaW4gdGhlIApzaWRlYmFyLCBhbmQgdGhlIG51bWJlciBvZiBzYW1wbGVzIGluIGVhY2ggY29uZGl0aW9uIGFuZCBiYXRjaCBjYW4gYmUgY2hhbmdlZCAKYnkgbW9kaWZ5aW5nIHRoZSB0YWJsZSBpbiB0aGUgdG9wLWxlZnQgY29ybmVyLiAKClRoZSBgQW5hbHlzaXMgYXBwcm9hY2hgIHNlY3Rpb24gb2YgdGhlIHNpZGViYXIgZGV0ZXJtaW5lcyBob3cgdGhlIGdlbmVyYXRlZCAKZGF0YSB3aWxsIGJlIGFuYWx5c2VkLiBJbiBlYWNoIGNhc2UsIGEgbGluZWFyIG1vZGVsIHdpbGwgYmUgdXNlZCB0byBjb21wYXJlIHRoZSAKdmFsdWVzIGZvciBlYWNoIGZlYXR1cmUgYmV0d2VlbiB0aGUgdHdvIGNvbmRpdGlvbnMsIGJ1dCBkZXBlbmRpbmcgb24gdGhlIApjaG9pY2UsIHRoZSBiYXRjaCBlZmZlY3Qgd2lsbCBiZSBhY2NvdW50ZWQgZm9yIGluIGRpZmZlcmVudCB3YXlzLiBBZ2FpbiwgbW9yZSAKZGV0YWlscyBjYW4gYmUgb2J0YWluZWQgYnkgY2xpY2tpbmcgb24gdGhlIGBEYXRhIGRlc2NyaXB0aW9uYCBidXR0b24uIAoKVGhlIGZpcnN0IHJvdyBvZiBwYW5lbHMgaW4gdGhlIG1haW4gd2luZG93IGlsbHVzdHJhdGUgdGhlIHJlc3VsdHMgb2YgdGhlIApzdGF0aXN0aWNhbCB0ZXN0LiBUaGUgbGVmdCBwYW5lbCBzaG93cyB0aGUgbnVtYmVyIG9mIGZlYXR1cmVzIGluIGVhY2ggCmNhdGVnb3J5IChhZmZlY3RlZCBieSBjb25kaXRpb24gZWZmZWN0IGFuZC9vciBieSBiYXRjaCBlZmZlY3QpLCBhbmQgdGhlIApmZWF0dXJlcyB0aGF0IGFyZSBkZXRlY3RlZCBhcyBzaWduaWZpY2FudGx5IGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBiZXR3ZWVuIAp0aGUgY29uZGl0aW9ucyBhcmUgY29sb3JlZCBpbiByZWQuIFRoZSByaWdodCBwYW5lbCBzaG93cyBhIGhpc3RvZ3JhbSBvZiBhbGwgdGhlIAoxLDAwMCBwLXZhbHVlcy4gSWYgdGhlcmUgaXMgbm8gc2lnbmFsIGluIHRoZSBkYXRhIChubyB0cnVseSBkaWZmZXJlbnRpYWxseSAKZXhwcmVzc2VkIGZlYXR1cmVzKSB3ZSBleHBlY3QgdGhpcyBoaXN0b2dyYW0gdG8gc2hvdyBhIHVuaWZvcm0gZGlzdHJpYnV0aW9uLiAKSWYgdGhlcmUgYXJlIHNvbWUgdHJ1bHkgZGlmZmVyZW50aWFsIGZlYXR1cmVzLCB3ZSBleHBlY3QgYWRkaXRpb25hbGx5IGEgcGVhayAKY2xvc2UgdG8gcD0wLiBBIHBlYWsgY2xvc2UgdG8gcD0xIG9mdGVuIGluZGljYXRlcyBhIHByb2JsZW0gd2l0aCB0aGUgZGF0YSwgZS5nLiwgCmEgZmFjdG9yIHRoYXQgYWZmZWN0cyB0aGUgZGF0YSBidXQgdGhhdCBpcyBub3QgYWNjb3VudGVkIGZvciBpbiB0aGUgYW5hbHlzaXMuIAoKVGhlIG5leHQgcGFuZWwgKGBTdW1tYXJ5YCkgcHJvdmlkZXMgYSBzdW1tYXJ5IG9mIHRoZSBzdGF0aXN0aWNhbCB0ZXN0IHJlc3VsdHMuIApNb3JlIHByZWNpc2VseSwgaXQgc2hvd3MgdGhlIFRQUiAodHJ1ZSBwb3NpdGl2ZSByYXRlLCBvciBwb3dlciAtIHRoZSBmcmFjdGlvbiAKb2YgdGhlIGZlYXR1cmVzIHRoYXQgYXJlIHRydWx5IGFmZmVjdGVkIGJ5IHRoZSBjb25kaXRpb24gdGhhdCBhcmUgZGV0ZWN0ZWQgCmFzIHNpZ25pZmljYW50bHkgZGlmZmVyZW50aWFsKSwgdGhlIEZEUCAoZmFsc2UgZGlzY292ZXJ5IHByb3BvcnRpb24gLSB0aGUgCmZyYWN0aW9uIG9mIHRoZSBmZWF0dXJlcyBkZXRlY3RlZCBhcyBzaWduaWZpY2FudCB0aGF0IGFyZSBmYWxzZSBwb3NpdGl2ZXMpLCAKYW5kIHRoZSBGUFIgKGZhbHNlIHBvc2l0aXZlIHJhdGUgLSB0aGUgZnJhY3Rpb24gb2YgdGhlIHRydWx5IG5vbi1kaWZmZXJlbnRpYWwgCmZlYXR1cmVzIHRoYXQgYXJlIG5ldmVydGhlbGVzcyBmb3VuZCB0byBiZSBzaWduaWZpY2FudCkuIAoKVGhlIGBEYXRhIGhlYXRtYXBgIHNob3dzIHRoZSBzaW11bGF0ZWQgZGF0YSBpbiB0aGUgZm9ybSBvZiBhIGhlYXRtYXAuIEl0IGFsc28gCmluZGljYXRlcyB0aGUgY29uZGl0aW9uIGFuZCBiYXRjaCBhc3NpZ25tZW50IG9mIHRoZSBzYW1wbGVzIChoZXJlIHNob3duIGluIHRoZSAKcm93cyksIGFuZCB0aGUgdHJ1ZSBkZXNpZ25hdGlvbiBvZiB0aGUgZmVhdHVyZXMgKGJhdGNoLSBhbmQvb3IgCmNvbmRpdGlvbi1hZmZlY3RlZCkgYXMgd2VsbCBhcyB3aGV0aGVyIHRoZXkgYXJlIGRldGVjdGVkIGFzIHNpZ25pZmljYW50bHkgCmRpZmZlcmVudGlhbCBiZXR3ZWVuIHRoZSB0d28gY29uZGl0aW9ucy4gCgpGaW5hbGx5LCB0aGUgbGFzdCByb3cgb2YgcGFuZWxzIHNob3cgZXhhbXBsZXMgZnJvbSBlYWNoIG9mIHRoZSBmb3VyIApjYXRlZ29yaWVzIG9mIGZlYXR1cmVzIChzYW1lIGNhdGVnb3JpZXMgYXMgaW4gdGhlIHRvcC1yaWdodCBwYW5lbCkuIEEgbmV3IHNldCAKb2YgZXhhbXBsZXMgY2FuIGJlIG9idGFpbmVkIGJ5IGNsaWNraW5nIG9uIHRoZSBgTmV3IHNlbGVjdGlvbmAgYnV0dG9uLiAKCiMgTm9uLWNvbmZvdW5kZWQgc2V0dXAKCkZpcnN0LCB3ZSBjb25zaWRlciB0aGUgc2V0dXAgd2hlcmUgdGhlcmUgaXMgbm8gY29uZm91bmRpbmcgYmV0d2VlbiB0aGUgY29uZGl0aW9uIChncm91cCkgYW5kIHRoZSBiYXRjaC4gCkluIG90aGVyIHdvcmRzLCBlYWNoIGNvbmRpdGlvbiBpcyBlcXVhbGx5IHJlcHJlc2VudGVkIGluIGVhY2ggb2YgdGhlIGJhdGNoZXMuIApXZSB3aWxsIGFzc3VtZSB0aGF0IDI1JSBvZiB0aGUgZmVhdHVyZXMgYXJlIHRydWx5IGRpZmZlcmVudCBiZXR3ZWVuIHRoZSB0d28gY29uZGl0aW9ucywgYW5kIHRoYXQgNTAlIG9mIHRoZSBmZWF0dXJlcyBhcmUgYWZmZWN0ZWQgYnkgdGhlIGJhdGNoLiAKVGhlIHR3byBzZXRzIG9mIGdlbmVzIG92ZXJsYXAgcGFydGx5LiAKVGh1cywgc29tZSBmZWF0dXJlcyB0aGF0IGFyZSB0cnVseSBkaWZmZXJlbnQgYmV0d2VlbiBjb25kaXRpb25zIGFyZSBhbHNvIGFmZmVjdGVkIGJ5IHRoZSBiYXRjaC4gCgojIyBEb24ndCBhZGp1c3QgZm9yIHRoZSBiYXRjaCBlZmZlY3QKCldlIGZpcnN0IGNvbnNpZGVyIHRoZSByZXN1bHRzIGlmIHdlIGRvbid0IGFkanVzdCBmb3IgdGhlIGJhdGNoIGVmZmVjdCBhdCBhbGwsIGFuZCBqdXN0IGNvbXBhcmUgdGhlIHR3byBjb25kaXRpb25zLiAKU2luY2UgZWFjaCBjb25kaXRpb24gY29udGFpbnMgc2FtcGxlcyBmcm9tIGJvdGggYmF0Y2hlcywgdGhlIHdpdGhpbi1jb25kaXRpb24gdmFyaWFuY2UgZXN0aW1hdGUgd2lsbCBhbHNvIGluY29ycG9yYXRlIHRoZSBkaWZmZXJlbmNlcyBpbmR1Y2VkIGJ5IHRoZSBiYXRjaCBlZmZlY3QsIGFuZCB0aHVzIGJlIGFydGlmaWNpYWxseSBpbmZsYXRlZCBjb21wYXJlZCB0byB0aGUgJ3RydWUnICh3aXRoaW4tYmF0Y2gpIHdpdGhpbi1jb25kaXRpb24gdmFyaWFuY2UuIApUaGUgZWZmZWN0IG9uIHRoZSByZXN1bHRzIG9mIHRoZSB0ZXN0IGNvbXBhcmluZyB0aGUgdHdvIGNvbmRpdGlvbnMgaXMgdGhhdCB3ZSB3aWxsIGhhdmUgbG93ZXIgcG93ZXIgdG8gZGV0ZWN0IHRydWUgZGlmZmVyZW5jZXMgKGR1ZSB0byB0aGUgb3ZlcmVzdGltYXRlZCB2YXJpYW5jZSkuCgpgYGB7cn0KYXBwIDwtIENvbmZvdW5kaW5nRXhwbG9yZXIoCiAgICBzYW1wbGVTaXplcyA9IG1hdHJpeChyZXAoNSwgNCksIG5yb3cgPSAyLAogICAgICAgICAgICAgICAgICAgICAgICAgZGltbmFtZXMgPSBsaXN0KGMoImdyb3VwMSIsICJncm91cDIiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjKCJiYXRjaDEiLCAiYmF0Y2gyIikpKSwKICAgIGZyYWNWYXJDb25kID0gMC4yNSwKICAgIGZyYWNWYXJCYXRjaCA9IDAuNSwKICAgIGNvbmRFZmZlY3RTaXplID0gNCwKICAgIGJhdGNoRWZmZWN0U2l6ZSA9IDQsCiAgICBhbmFseXNpc0FwcHJvYWNoID0gImRvbnRBZGp1c3QiCikKYGBgCgpgYGB7ciwgZWNobz1GQUxTRX0KU0NSRUVOU0hPVCgic2NyZWVuc2hvdHMvY29uZm91bmRpbmdleHBsb3Jlci01LTUtNS01LTAuMjUtMC41LTQtNC1kb250QWRqdXN0LnBuZyIsCiAgICAgICAgICAgdndpZHRoID0gMTMwMCkKYGBgCgojIyBJbmNyZWFzZSB0aGUgc2l6ZSBvZiB0aGUgYmF0Y2ggZWZmZWN0LCBkb24ndCBhZGp1c3QKClRvIHNlZSB0aGUgZWZmZWN0IGRlc2NyaWJlZCBhYm92ZSBldmVuIGNsZWFyZXIsIGxldCdzIGluY3JlYXNlIHRoZSBzaXplIG9mIHRoZSBiYXRjaCBlZmZlY3QuIApUaGlzIHNpbXVsYXRlcyBhIHNpdHVhdGlvbiB3aGVyZSwgZm9yIGV4YW1wbGUsIGZpdmUgcmVwbGljYXRlcyBvZiBlYWNoIGNvbmRpdGlvbiB3ZXJlIG9idGFpbmVkIGJ5IGVhY2ggb2YgdHdvIHRlY2hub2xvZ2llcywgb3IgdXNpbmcgY29tcGxldGVseSBkaWZmZXJlbnQgcHJvdG9jb2xzLgpOb3cgd2Ugc2VlIHRoZSBsb3NzIG9mIHBvd2VyIG11Y2ggbW9yZSBzdHJpa2luZ2x5LCBhbmQgd2Ugbm90aWNlIGEgc3RyYW5nZWx5IHNoYXBlZCBwLXZhbHVlIGhpc3RvZ3JhbS4gCgpgYGB7cn0KYXBwIDwtIENvbmZvdW5kaW5nRXhwbG9yZXIoCiAgICBzYW1wbGVTaXplcyA9IG1hdHJpeChyZXAoNSwgNCksIG5yb3cgPSAyLAogICAgICAgICAgICAgICAgICAgICAgICAgZGltbmFtZXMgPSBsaXN0KGMoImdyb3VwMSIsICJncm91cDIiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjKCJiYXRjaDEiLCAiYmF0Y2gyIikpKSwKICAgIGZyYWNWYXJDb25kID0gMC4yNSwKICAgIGZyYWNWYXJCYXRjaCA9IDAuNSwKICAgIGNvbmRFZmZlY3RTaXplID0gNCwKICAgIGJhdGNoRWZmZWN0U2l6ZSA9IDEwLAogICAgYW5hbHlzaXNBcHByb2FjaCA9ICJkb250QWRqdXN0IgopCmBgYAoKYGBge3IsIGVjaG89RkFMU0V9ClNDUkVFTlNIT1QoInNjcmVlbnNob3RzL2NvbmZvdW5kaW5nZXhwbG9yZXItNS01LTUtNS0wLjI1LTAuNS00LTEwLWRvbnRBZGp1c3QucG5nIiwKICAgICAgICAgICB2d2lkdGggPSAxMzAwKQpgYGAKCiMjIEFkanVzdCBmb3IgdGhlIGJhdGNoIGVmZmVjdCBieSBpbmNsdWRpbmcgaXQgYXMgYSBjb3ZhcmlhdGUKClRoZSByZWNvbW1lbmRlZCB3YXkgb2YgYWRqdXN0aW5nIGZvciBiYXRjaCBlZmZlY3RzIGluIHN0YXRpc3RpY2FsIG1vZGVsaW5nIGlzIHRvIGluY2x1ZGUgdGhlbSBhcyBhIGNvdmFyaWF0ZSAoYW4gJ2FkZGl0aW9uYWwgcHJlZGljdG9yIHZhcmlhYmxlJykgaW4gdGhlIG1vZGVsLiAKSGVyZSB3ZSBmb2xsb3cgdGhpcyBhcHByb2FjaCwgYW5kIHRodXMgbW9kZWwgdGhlIG9ic2VydmVkIHZhbHVlcyBhcyBhIGxpbmVhciBjb21iaW5hdGlvbiBvZiB0aGUgZXN0aW1hdGVkIGJhdGNoIGVmZmVjdCBhbmQgdGhlIGVzdGltYXRlZCBjb25kaXRpb24gZWZmZWN0LiAKQXMgYmVmb3JlLCB0aGUgcC12YWx1ZXMgY29ycmVzcG9uZCB0byB0aGUgdGVzdCBvZiB0aGUgY29uZGl0aW9uIGVmZmVjdCAoYnV0IG5vdyB3ZSdyZSBhY2NvdW50aW5nIGZvciB0aGUgYmF0Y2ggZWZmZWN0KS4KV2Ugbm90ZSB0aGF0IGJ5IGluY2x1ZGluZyB0aGUgZXh0cmEgcHJlZGljdG9yIGNvcnJlc3BvbmRpbmcgdG8gdGhlIGJhdGNoIGVmZmVjdCwgd2UgY2FuIGV4cGxhaW4gc29tZSBvZiB0aGUgYmV0d2Vlbi1zYW1wbGUgdmFyaWFuY2UgYW5kIHRodXMgaW5jcmVhc2UgdGhlIHBvd2VyIHRvIGRldGVjdCBkaWZmZXJlbmNlcy4gCgpgYGB7cn0KYXBwIDwtIENvbmZvdW5kaW5nRXhwbG9yZXIoCiAgICBzYW1wbGVTaXplcyA9IG1hdHJpeChyZXAoNSwgNCksIG5yb3cgPSAyLAogICAgICAgICAgICAgICAgICAgICAgICAgZGltbmFtZXMgPSBsaXN0KGMoImdyb3VwMSIsICJncm91cDIiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjKCJiYXRjaDEiLCAiYmF0Y2gyIikpKSwKICAgIGZyYWNWYXJDb25kID0gMC4yNSwKICAgIGZyYWNWYXJCYXRjaCA9IDAuNSwKICAgIGNvbmRFZmZlY3RTaXplID0gNCwKICAgIGJhdGNoRWZmZWN0U2l6ZSA9IDQsCiAgICBhbmFseXNpc0FwcHJvYWNoID0gImluY2xCYXRjaCIKKQpgYGAKCmBgYHtyLCBlY2hvPUZBTFNFfQpTQ1JFRU5TSE9UKCJzY3JlZW5zaG90cy9jb25mb3VuZGluZ2V4cGxvcmVyLTUtNS01LTUtMC4yNS0wLjUtNC00LWluY2xCYXRjaC5wbmciLAogICAgICAgICAgIHZ3aWR0aCA9IDEzMDApCmBgYAoKIyBGdWxseSBjb25mb3VuZGVkIHNldHVwCgpOZXh0LCB3ZSBjb25zaWRlciBhIGZ1bGx5IGNvbmZvdW5kZWQgc2V0dXAuCkhlcmUsIHRoZSAxMCByZXBsaWNhdGVzIG9mIGVhY2ggY29uZGl0aW9uIHdlcmUgYWxsIGFjcXVpcmVkIGluIHRoZSBzYW1lIGJhdGNoLCBhbmQgb25seSBzYW1wbGVzIGZyb20gb25lIGNvbmRpdGlvbiB3ZXJlIGluY2x1ZGVkIGluIGVhY2ggYmF0Y2guIAoKIyMgRG9uJ3QgYWRqdXN0IGZvciB0aGUgYmF0Y2ggZWZmZWN0CgpJZiB3ZSBkbyBub3QgYWRqdXN0IGZvciB0aGUgYmF0Y2ggZWZmZWN0IGluIHRoaXMgY2FzZSwgYm90aCB0aGUgYmF0Y2ggYW5kIGNvbmRpdGlvbiBlZmZlY3RzIGFyZSBpbnRlcnByZXRlZCBhcyB0aGUgY29uZGl0aW9uIGVmZmVjdCwgd2hpY2ggbGVhZHMgdG8gYSBoaWdoIGZhbHNlIHBvc2l0aXZlIHJhdGUuCkluIG90aGVyIHdvcmRzLCBtYW55IGZlYXR1cmVzIGFyZSBpbnRlcnByZXRlZCBhcyBiZWluZyBkaWZmZXJlbnQgYmV0d2VlbiB0aGUgdHdvIGNvbmRpdGlvbnMsIHdoZXJlYXMgdGhlIGRpZmZlcmVuY2UgaW4gcmVhbGl0eSBpcyBjYXVzZWQgYnkgdGhlIGJhdGNoIGVmZmVjdCwgd2hpY2ggaXMgY29tcGxldGVseSBjb25mb3VuZGVkIHdpdGggdGhlIGNvbmRpdGlvbi4gCkluIGFkZGl0aW9uLCBzb21lIGZlYXR1cmVzIHRoYXQgYXJlIHRydWx5IGRpZmZlcmVudGlhbCBiZXR3ZWVuIHRoZSB0d28gY29uZGl0aW9ucyBhcmUgbm8gbG9uZ2VyIGRldGVjdGVkLCBzaW5jZSB0aGUgZGlmZmVyZW5jZSBpcyBjb3VudGVyYWN0ZWQgYnkgdGhlIGJhdGNoIGVmZmVjdC4KCmBgYHtyfQphcHAgPC0gQ29uZm91bmRpbmdFeHBsb3JlcigKICAgIHNhbXBsZVNpemVzID0gbWF0cml4KGMoMTAsIDAsIDAsIDEwKSwgbnJvdyA9IDIsCiAgICAgICAgICAgICAgICAgICAgICAgICBkaW1uYW1lcyA9IGxpc3QoYygiZ3JvdXAxIiwgImdyb3VwMiIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGMoImJhdGNoMSIsICJiYXRjaDIiKSkpLAogICAgZnJhY1ZhckNvbmQgPSAwLjI1LAogICAgZnJhY1ZhckJhdGNoID0gMC41LAogICAgY29uZEVmZmVjdFNpemUgPSA0LAogICAgYmF0Y2hFZmZlY3RTaXplID0gNCwKICAgIGFuYWx5c2lzQXBwcm9hY2ggPSAiZG9udEFkanVzdCIKKQpgYGAKCmBgYHtyLCBlY2hvPUZBTFNFfQpTQ1JFRU5TSE9UKCJzY3JlZW5zaG90cy9jb25mb3VuZGluZ2V4cGxvcmVyLTEwLTAtMC0xMC0wLjI1LTAuNS00LTQtZG9udEFkanVzdC5wbmciLAogICAgICAgICAgIHZ3aWR0aCA9IDEzMDApCmBgYAoKIyMgQXR0ZW1wdCB0byBhZGp1c3QgZm9yIHRoZSBiYXRjaCBlZmZlY3QKCkluIHRoZSBub24tY29uZm91bmRlZCBzaXR1YXRpb24gYWJvdmUsIHdlIGNvdWxkIGFkanVzdCBmb3IgdGhlIGJhdGNoIGVmZmVjdCBieSBpbmNsdWRpbmcgaXQgYXMgYSBjb3ZhcmlhdGUgaW4gdGhlIG1vZGVsLiAKV2hlbiB0aGUgYmF0Y2ggYW5kIGNvbmRpdGlvbiBmYWN0b3JzIGFyZSBmdWxseSBjb25mb3VuZGVkLCBob3dldmVyLCB0aGlzIGlzIG5vdCBwb3NzaWJsZSwgc2luY2UgaXQgd291bGQgbWVhbiBpbmNsdWRpbmcgdHdvIHBlcmZlY3RseSBjb3JyZWxhdGVkIHByZWRpY3RvcnMgaW4gdGhlIG1vZGVsLiAKCmBgYHtyfQphcHAgPC0gQ29uZm91bmRpbmdFeHBsb3JlcigKICAgIHNhbXBsZVNpemVzID0gbWF0cml4KGMoMTAsIDAsIDAsIDEwKSwgbnJvdyA9IDIsCiAgICAgICAgICAgICAgICAgICAgICAgICBkaW1uYW1lcyA9IGxpc3QoYygiZ3JvdXAxIiwgImdyb3VwMiIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGMoImJhdGNoMSIsICJiYXRjaDIiKSkpLAogICAgZnJhY1ZhckNvbmQgPSAwLjI1LAogICAgZnJhY1ZhckJhdGNoID0gMC41LAogICAgY29uZEVmZmVjdFNpemUgPSA0LAogICAgYmF0Y2hFZmZlY3RTaXplID0gNCwKICAgIGFuYWx5c2lzQXBwcm9hY2ggPSAiaW5jbEJhdGNoIgopCmBgYAoKYGBge3IsIGVjaG89RkFMU0V9ClNDUkVFTlNIT1QoInNjcmVlbnNob3RzL2NvbmZvdW5kaW5nZXhwbG9yZXItMTAtMC0wLTEwLTAuMjUtMC41LTQtNC1pbmNsQmF0Y2gucG5nIiwKICAgICAgICAgICB2d2lkdGggPSAxMzAwKQpgYGAKCiMgUGFydGx5IGNvbmZvdW5kZWQgc2V0dXAKCkZpbmFsbHksIHdlIGNvbnNpZGVyIGEgcGFydGx5IGNvbmZvdW5kZWQgc2V0dXAuIEhlcmUsIGVhY2ggYmF0Y2ggY29udGFpbnMgOSBzYW1wbGVzIG9mIG9uZSBjb25kaXRpb24gYW5kIDEgc2FtcGxlIG9mIHRoZSBvdGhlciBjb25kaXRpb24uIAoKIyMgQWRqdXN0IGZvciB0aGUgYmF0Y2ggZWZmZWN0IGJ5IGluY2x1ZGluZyBpdCBhcyBhIGNvdmFyaWF0ZQoKQXMgaW4gdGhlIG5vbi1jb25mb3VuZGVkIGNhc2UsIHdlIGF0dGVtcHQgdG8gYWRqdXN0IGZvciB0aGUgYmF0Y2ggZWZmZWN0IGJ5IGluY2x1ZGluZyBpdCBhcyBhIGNvdmFyaWF0ZSBpbiB0aGUgbW9kZWwuIApXaGlsZSBpdCBpcyBub3cgc3RhdGlzdGljYWxseSBmZWFzaWJsZSAoYXMgb3Bwb3NlZCB0byB0aGUgZnVsbHkgY29uZm91bmRlZCBjYXNlKSwgd2Ugbm90aWNlIHRoYXQgdGhlIHBvd2VyIHRvIGRldGVjdCB0cnVlIGRpZmZlcmVuY2VzIGJldHdlZW4gdGhlIGNvbmRpdGlvbnMgaXMgbG93ZXIgdGhhbiBpbiB0aGUgbm9uLWNvbmZvdW5kZWQgY2FzZS4gCgpgYGB7cn0KYXBwIDwtIENvbmZvdW5kaW5nRXhwbG9yZXIoCiAgICBzYW1wbGVTaXplcyA9IG1hdHJpeChjKDksIDEsIDEsIDkpLCBucm93ID0gMiwKICAgICAgICAgICAgICAgICAgICAgICAgIGRpbW5hbWVzID0gbGlzdChjKCJncm91cDEiLCAiZ3JvdXAyIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYygiYmF0Y2gxIiwgImJhdGNoMiIpKSksCiAgICBmcmFjVmFyQ29uZCA9IDAuMjUsCiAgICBmcmFjVmFyQmF0Y2ggPSAwLjUsCiAgICBjb25kRWZmZWN0U2l6ZSA9IDQsCiAgICBiYXRjaEVmZmVjdFNpemUgPSA0LAogICAgYW5hbHlzaXNBcHByb2FjaCA9ICJpbmNsQmF0Y2giCikKYGBgCgpgYGB7ciwgZWNobz1GQUxTRX0KU0NSRUVOU0hPVCgic2NyZWVuc2hvdHMvY29uZm91bmRpbmdleHBsb3Jlci05LTEtMS05LTAuMjUtMC41LTQtNC1pbmNsQmF0Y2gucG5nIiwKICAgICAgICAgICB2d2lkdGggPSAxMzAwKQpgYGAKCiMjIFJlbW92ZSB0aGUgYmF0Y2ggZWZmZWN0IGluIGFkdmFuY2UKCkFub3RoZXIgYXBwcm9hY2ggdGhhdCBpcyBzb21ldGltZXMgdXNlZCwgZXNwZWNpYWxseSB3aGVuIHRoZSBkYXRhIGlzIGdvaW5nIHRvIGJlIHVzZWQgZm9yIGV4cGxvcmF0b3J5IGFuYWx5c2lzIHJhdGhlciB0aGFuIHN0YXRpc3RpY2FsIG1vZGVsaW5nLCBpcyB0byBlc3RpbWF0ZSB0aGUgYmF0Y2ggZWZmZWN0IGluIGFkdmFuY2UsIGFuZCBzdWJ0cmFjdCBpdCBmcm9tIHRoZSBvYnNlcnZlZCB2YWx1ZXMuIApIZXJlIHdlIGlsbHVzdHJhdGUgdGhpcyBhcHByb2FjaCBmb3IgdGhlIHBhcnRseSBjb25mb3VuZGVkIHNldHVwLiAKV2Ugbm90aWNlIHRoYXQgYWxtb3N0IGFsbCB0aGUgdHJ1ZSBzaWduYWwgaXMgcmVtb3ZlZCBieSB0aGlzIHByb2NlZHVyZSwgc2luY2Ugd2UgZG8gbm90IGluZm9ybSB0aGUgYmF0Y2ggZWZmZWN0IGNvcnJlY3Rpb24gc3RlcCBvZiB0aGUgY29uZGl0aW9uIGVmZmVjdCwgYW5kIHRodXMgYW55IHNpZ25hbCB0aGF0IGNvdWxkIHBvdGVudGlhbGx5IGJlIGV4cGxhaW5lZCBieSB0aGUgYmF0Y2ggdmFyaWFibGUgd2lsbCBiZSByZW1vdmVkLiAKCmBgYHtyfQphcHAgPC0gQ29uZm91bmRpbmdFeHBsb3JlcigKICAgIHNhbXBsZVNpemVzID0gbWF0cml4KGMoOSwgMSwgMSwgOSksIG5yb3cgPSAyLAogICAgICAgICAgICAgICAgICAgICAgICAgZGltbmFtZXMgPSBsaXN0KGMoImdyb3VwMSIsICJncm91cDIiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjKCJiYXRjaDEiLCAiYmF0Y2gyIikpKSwKICAgIGZyYWNWYXJDb25kID0gMC4yNSwKICAgIGZyYWNWYXJCYXRjaCA9IDAuNSwKICAgIGNvbmRFZmZlY3RTaXplID0gNCwKICAgIGJhdGNoRWZmZWN0U2l6ZSA9IDQsCiAgICBhbmFseXNpc0FwcHJvYWNoID0gInJlbW92ZUJhdGNoIgopCmBgYAoKYGBge3IsIGVjaG89RkFMU0V9ClNDUkVFTlNIT1QoInNjcmVlbnNob3RzL2NvbmZvdW5kaW5nZXhwbG9yZXItOS0xLTEtOS0wLjI1LTAuNS00LTQtcmVtb3ZlQmF0Y2gucG5nIiwKICAgICAgICAgICB2d2lkdGggPSAxMzAwKQpgYGAKCiMjIFJlbW92ZSBiYXRjaCBlZmZlY3QgaW4gYWR2YW5jZSwgYWNjb3VudGluZyBmb3IgdGhlIGNvbmRpdGlvbgoKQXMgYW4gYWx0ZXJuYXRpdmUgdG8gdGhlIGFwcHJvYWNoIG91dGxpbmVkIGluIHRoZSBwcmV2aW91cyBzZWN0aW9uLCB3ZSBjb3VsZCBpbmZvcm0gdGhlIGJhdGNoIGVmZmVjdCBhZGp1c3RtZW50IHN0ZXAgb2YgdGhlIGNvbmRpdGlvbiBmYWN0b3IsIGFuZCBpbnN0cnVjdCBpdCB0byBub3QgcmVtb3ZlIGFueSBlZmZlY3QgdGhhdCBjb3VsZCBiZSBleHBsYWluZWQgYnkgdGhlIGxhdHRlci4gClNpbmNlIHRoZSBiYXRjaCBhbmQgY29uZGl0aW9uIGZhY3RvcnMgYXJlIGhpZ2hseSBzaW1pbGFyLCB0aGUgZWZmZWN0IG9mIHRoaXMgaXMgdGhhdCB2ZXJ5IGxpdHRsZSBvZiB0aGUgYmF0Y2ggZWZmZWN0IGlzIGluIGZhY3QgZWxpbWluYXRlZC4gCgpgYGB7cn0KYXBwIDwtIENvbmZvdW5kaW5nRXhwbG9yZXIoCiAgICBzYW1wbGVTaXplcyA9IG1hdHJpeChjKDksIDEsIDEsIDkpLCBucm93ID0gMiwKICAgICAgICAgICAgICAgICAgICAgICAgIGRpbW5hbWVzID0gbGlzdChjKCJncm91cDEiLCAiZ3JvdXAyIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYygiYmF0Y2gxIiwgImJhdGNoMiIpKSksCiAgICBmcmFjVmFyQ29uZCA9IDAuMjUsCiAgICBmcmFjVmFyQmF0Y2ggPSAwLjUsCiAgICBjb25kRWZmZWN0U2l6ZSA9IDQsCiAgICBiYXRjaEVmZmVjdFNpemUgPSA0LAogICAgYW5hbHlzaXNBcHByb2FjaCA9ICJyZW1vdmVCYXRjaEFjY0NvbmQiCikKYGBgCgpgYGB7ciwgZWNobz1GQUxTRX0KU0NSRUVOU0hPVCgic2NyZWVuc2hvdHMvY29uZm91bmRpbmdleHBsb3Jlci05LTEtMS05LTAuMjUtMC41LTQtNC1yZW1vdmVCYXRjaEFjY0NvbmQucG5nIiwKICAgICAgICAgICB2d2lkdGggPSAxMzAwKQpgYGAKCiMgQ29uY2x1c2lvbgoKVGhlIGV4YW1wbGVzIGFib3ZlIGlsbHVzdHJhdGUgdGhlIGNvbXBsaWNhdGlvbnMgdGhhdCBhcmlzZSB3aGVuIHRoZSBjb25kaXRpb24gb2YgaW50ZXJlc3QgaXMgKHBhcnRseSBvciBmdWxseSkgY29uZm91bmRlZCB3aXRoIGEgYmF0Y2ggZWZmZWN0IG9yIGFub3RoZXIgc291cmNlIG9mIHVud2FudGVkIHZhcmlhdGlvbiBhZmZlY3RpbmcgdGhlIGRhdGEuIApJdCBpcyBpbXBvcnRhbnQgdG8gbm90ZSB0aGF0IGluIGEgZnVsbHkgY29uZm91bmRlZCBleHBlcmltZW50YWwgc2V0dXAsIHN0YXRpc3RpY2FsIG1vZGVsaW5nIGNhbiBuZXZlciAncmVzY3VlJyB0aGUgZXhwZXJpbWVudCwgYW5kIHRoZXJlIGlzIG5vIHJlbGlhYmxlIHdheSBvZiBkaXN0aW5ndWlzaGluZyB0aGUgZWZmZWN0IG9mIHRoZSBiYXRjaCBhbmQgdGhlIGNvbmRpdGlvbiwgcmVzcGVjdGl2ZWx5LiAKSW4gYSBub24tY29uZm91bmRlZCBleHBlcmltZW50LCBldmVuIGlmIHRoZXJlIGlzIGEgYmF0Y2ggZWZmZWN0LCBpdCBjYW4gdHlwaWNhbGx5IGJlIGFjY291bnRlZCBmb3IgaW4gdGhlIHN0YXRpc3RpY2FsIG1vZGVsaW5nLiAKCiMgU2Vzc2lvbiBpbmZvCgpgYGB7cn0Kc2Vzc2lvbkluZm8oKQpgYGAK