diff --git a/graphics_bioinf.R b/graphics_bioinf.R
index 7de8b1a7f09e7c53e1e4698d2c225569ba62cb73..c53951551f1f28c06962a781e120c64b9d7c2035 100644
--- a/graphics_bioinf.R
+++ b/graphics_bioinf.R
@@ -36,6 +36,9 @@ library("limma")
library("Single.mTEC.Transcriptomes")
library("DESeq2")
library("tibble")
+library("broom")
+library("scran")
+library("locfit")
theme_set(theme_gray(base_size = 18))
@@ -138,6 +141,36 @@ scatter_tra +
lm_tra <- lm(tra ~ total_detected, data = tra_detected)
lm_tra
+## ----loessExampleLinFit, dependson="fit_model"---------------------------
+
+# create_data
+y <- seq(from=1, to=10, length.out=100)
+a <- y^3 +y^2 + rnorm(100,mean=0, sd=30)
+dataL <- data.frame(a=a, y=y)
+qplot(y, a, data = dataL)
+
+# linear fit
+linreg <- lm(a~y, data = dataL)
+
+
+(qplot(y, a, data = dataL) +
+ geom_abline(slope = tidy(linreg, quick = TRUE)[2,2],
+ intercept = tidy(linreg, quick = TRUE)[1,2]))
+tidy(linreg)
+
+dataL$LinReg <- predict(linreg)
+
+## ----loessExampleFit, dependson="loessExampleLinFit"---------------------
+
+dataL$locFit <- predict(locfit(y~lp(a, nn=0.5, deg=1), data=dataL),
+ newdata = dataL$a)
+
+
+ (qplot(a, y, data = dataL, main = "Linear vs. local regression")
+ + geom_line(aes(x = a, y = locFit), color = "dodgerblue3")
+ + geom_line(aes(x = a, y = LinReg), color = "coral3"))
+
+
## ----session_info, cache = FALSE-----------------------------------------
sessionInfo()
diff --git a/graphics_bioinf.Rmd b/graphics_bioinf.Rmd
index af33a8b4a0b5a7a7f7415c7ad7bbf4bac5e7ce54..b06e6c715ba48bb163e4386c47ece2f3a8abec8a 100755
--- a/graphics_bioinf.Rmd
+++ b/graphics_bioinf.Rmd
@@ -64,6 +64,9 @@ library("limma")
library("Single.mTEC.Transcriptomes")
library("DESeq2")
library("tibble")
+library("broom")
+library("scran")
+library("locfit")
theme_set(theme_gray(base_size = 18))
@@ -311,13 +314,89 @@ We can of course always add more predictors to the linear function. The coeffici
\(b\) is called the __slope__ and \(a\) is called the __intercept__ .
We can fit a linear regression via a call to the function `lm()`. The regression
-model is specified using R's formula notation.
+model is specified using R's formula notation.
```{r regresssion_tra}
lm_tra <- lm(tra ~ total_detected, data = tra_detected)
lm_tra
```
+As we can see, the estimated
+slope is ~ `r tidy(lm_tra, quick = TRUE)[2, "estimate"]`, indicating
+that we have a proportion of `r tidy(lm_tra, quick = TRUE)[2, "estimate"]`
+tra expressing genes on average per cell for the highly variable genes.
+This is in line with the supplementary figure 1 of the original publication,
+which uses the full set of expressed genes, not just the highly variable
+ones.
+
+# Local regression (LOESS)
+
+
+
+Local regression is a commonly used approach for fitting flexible non--linear
+functions, which involves computing many local linear regression fits and combining
+them. Local regression is a very useful technique both for data visualization and
+trend fitting. Fitting many local models requires quite some computational power,
+but it usually feasible with today's hardware. We illustrate the local regression
+using the `r CRANpkg("locfit") ` package on simulated data.
+
+We first fit a linear regression line to simulated
+data that follows a polynomial trend and see that it does not really
+fit well.
+
+```{r loessExampleLinFit, dependson="fit_model"}
+
+# create_data
+y <- seq(from=1, to=10, length.out=100)
+a <- y^3 +y^2 + rnorm(100,mean=0, sd=30)
+dataL <- data.frame(a=a, y=y)
+qplot(y, a, data = dataL)
+
+# linear fit
+linreg <- lm(a~y, data = dataL)
+
+
+(qplot(y, a, data = dataL) +
+ geom_abline(slope = tidy(linreg, quick = TRUE)[2,2],
+ intercept = tidy(linreg, quick = TRUE)[1,2]))
+tidy(linreg)
+
+dataL$LinReg <- predict(linreg)
+```
+
+We now use the function `locfit` to perform a local regression on the
+data. It takes the predictors wrapped in a call to `lp()`. Within this
+function we can also set tunning parameters. An important one is the `nn`
+one, which set the proportion of nearest--neighbors to be used for the local fits.
+
+The lower this percentage, the more closely the line will follow the data points.
+
+```{r loessExampleFit, dependson="loessExampleLinFit"}
+
+dataL$locFit <- predict(locfit(y~lp(a, nn=0.5, deg=1), data=dataL),
+ newdata = dataL$a)
+
+
+ (qplot(a, y, data = dataL, main = "Linear vs. local regression")
+ + geom_line(aes(x = a, y = locFit), color = "dodgerblue3")
+ + geom_line(aes(x = a, y = LinReg), color = "coral3"))
+
+```
+
+
+
+# Normalization and confounding factors.
+
+
+## Normalization of single cell data
+
+* use scran size factors
+
+## Confounding factors
+
+* ZINBA Wave
+
+
# Session Info
diff --git a/graphics_bioinf.html b/graphics_bioinf.html
index 020488ad70565e53da22f35d0edd922e08c21c6f..8cd0dc0df951065ed8d204d27e4f4e4ed9da0f0e 100644
--- a/graphics_bioinf.html
+++ b/graphics_bioinf.html
@@ -11,7 +11,7 @@
-
+
Visual exploration for bioinformatics
@@ -164,7 +164,7 @@ $(document).ready(function () {
Visual exploration for bioinformatics
Bernd Klaus
-11 August 2017
+15 August 2017
@@ -178,7 +178,12 @@ $(document).ready(function () {
4.1 Exercise: Geometries and aspect ratios
5 Regression models
-6 Session Info
+6 Local regression (LOESS)
+7 Normalization and confounding factors.
+8 Session Info
@@ -187,7 +192,7 @@ To compile this document
graphics.off();rm(list=ls());rmarkdown::render('graphics_bioinf.Rmd');purl('graphics_bioinf.Rmd')
-->
LAST UPDATE AT
- [1] "Fri Aug 11 18:45:13 2017"
+ [1] "Tue Aug 15 16:38:33 2017"
Required packages and other preparations
library("readxl")
@@ -216,8 +221,11 @@ graphics.off();rm(list=ls());rmarkdown::render('graphics_bioinf.Rmd');purl('grap
library("Single.mTEC.Transcriptomes")
library("DESeq2")
library("tibble")
-
-theme_set(theme_gray(base_size = 18))
+library("broom")
+library("scran")
+library("locfit")
+
locfit 1.5-9.1 2013-03-22
+
theme_set(theme_gray(base_size = 18))
@@ -378,9 +386,59 @@ lm_tra
Coefficients:
(Intercept) total_detected
36.944 0.218
+
As we can see, the estimated slope is ~ 0.218, indicating that we have a proportion of 0.218 tra expressing genes on average per cell for the highly variable genes. This is in line with the supplementary figure 1 of the original publication, which uses the full set of expressed genes, not just the highly variable ones.
+
+
+
Local regression (LOESS)
+
Local regression is a commonly used approach for fitting flexible non–linear functions, which involves computing many local linear regression fits and combining them. Local regression is a very useful technique both for data visualization and trend fitting. Fitting many local models requires quite some computational power, but it usually feasible with today’s hardware. We illustrate the local regression using the locfit package on simulated data.
+
We first fit a linear regression line to simulated data that follows a polynomial trend and see that it does not really fit well.
+
# create_data
+y <- seq(from=1, to=10, length.out=100)
+a <- y^3 +y^2 + rnorm(100,mean=0, sd=30)
+dataL <- data.frame(a=a, y=y)
+qplot(y, a, data = dataL)
+

+
# linear fit
+linreg <- lm(a~y, data = dataL)
+
+
+(qplot(y, a, data = dataL) +
+ geom_abline(slope = tidy(linreg, quick = TRUE)[2,2],
+ intercept = tidy(linreg, quick = TRUE)[1,2]))
+

+
+
term estimate std.error statistic p.value
+ 1 (Intercept) -306 26.73 -11.4 9.82e-20
+ 2 y 114 4.39 25.9 1.36e-45
+
dataL$LinReg <- predict(linreg)
+
We now use the function locfit
to perform a local regression on the data. It takes the predictors wrapped in a call to lp()
. Within this function we can also set tunning parameters. An important one is the nn
one, which set the proportion of nearest–neighbors to be used for the local fits.
+
The lower this percentage, the more closely the line will follow the data points.
+
dataL$locFit <- predict(locfit(y~lp(a, nn=0.5, deg=1), data=dataL),
+ newdata = dataL$a)
+
+
+ (qplot(a, y, data = dataL, main = "Linear vs. local regression")
+ + geom_line(aes(x = a, y = locFit), color = "dodgerblue3")
+ + geom_line(aes(x = a, y = LinReg), color = "coral3"))
+

+
+
+
Normalization and confounding factors.
+
+
Normalization of single cell data
+
+- use scran size factors
+
+
+
+
Confounding factors
+
+
-
Session Info
+
Session Info
R version 3.4.1 (2017-06-30)
Platform: x86_64-pc-linux-gnu (64-bit)
@@ -403,61 +461,68 @@ lm_tra
[8] methods base
other attached packages:
- [1] bindrcpp_0.2 sva_3.24.4
- [3] genefilter_1.58.1 mgcv_1.8-18
- [5] nlme_3.1-131 BiocParallel_1.10.1
- [7] DESeq2_1.16.1 SummarizedExperiment_1.6.3
- [9] DelayedArray_0.2.7 Biobase_2.36.2
- [11] GenomicRanges_1.28.4 GenomeInfoDb_1.12.2
- [13] IRanges_2.10.2 S4Vectors_0.14.3
- [15] BiocGenerics_0.22.0 Single.mTEC.Transcriptomes_1.4.0
- [17] limma_3.32.5 openxlsx_4.0.17
- [19] car_2.1-5 corrplot_0.77
- [21] plotly_4.7.1 forcats_0.2.0
- [23] entropy_1.2.1 magrittr_1.5
- [25] factoextra_1.0.4 gtools_3.5.0
- [27] fdrtool_1.2.15 matrixStats_0.52.2
- [29] pheatmap_1.0.8 stringr_1.2.0
- [31] RColorBrewer_1.1-2 dplyr_0.7.2
- [33] purrr_0.2.3 readr_1.1.1
- [35] tidyr_0.6.3 tibble_1.3.3
- [37] ggplot2_2.2.1 tidyverse_1.1.1
- [39] BiocStyle_2.4.1 readxl_1.0.0
- [41] knitr_1.16
+ [1] sva_3.24.4 genefilter_1.58.1
+ [3] mgcv_1.8-18 nlme_3.1-131
+ [5] locfit_1.5-9.1 scran_1.4.5
+ [7] scater_1.4.0 broom_0.4.2
+ [9] bindrcpp_0.2 BiocParallel_1.10.1
+ [11] DESeq2_1.16.1 SummarizedExperiment_1.6.3
+ [13] DelayedArray_0.2.7 Biobase_2.36.2
+ [15] GenomicRanges_1.28.4 GenomeInfoDb_1.12.2
+ [17] IRanges_2.10.2 S4Vectors_0.14.3
+ [19] BiocGenerics_0.22.0 Single.mTEC.Transcriptomes_1.4.0
+ [21] limma_3.32.5 openxlsx_4.0.17
+ [23] car_2.1-5 corrplot_0.77
+ [25] plotly_4.7.1 forcats_0.2.0
+ [27] entropy_1.2.1 magrittr_1.5
+ [29] factoextra_1.0.4 gtools_3.5.0
+ [31] fdrtool_1.2.15 matrixStats_0.52.2
+ [33] pheatmap_1.0.8 stringr_1.2.0
+ [35] RColorBrewer_1.1-2 dplyr_0.7.2
+ [37] purrr_0.2.3 readr_1.1.1
+ [39] tidyr_0.6.3 tibble_1.3.3
+ [41] ggplot2_2.2.1 tidyverse_1.1.1
+ [43] BiocStyle_2.4.1 readxl_1.0.0
+ [45] knitr_1.16
loaded via a namespace (and not attached):
- [1] minqa_1.2.4 colorspace_1.3-2 rprojroot_1.2
- [4] htmlTable_1.9 XVector_0.16.0 base64enc_0.1-3
- [7] MatrixModels_0.4-1 bit64_0.9-7 ggrepel_0.6.5
- [10] AnnotationDbi_1.38.2 lubridate_1.6.0 xml2_1.1.1
- [13] codetools_0.2-15 splines_3.4.1 mnormt_1.5-5
- [16] geneplotter_1.54.0 Formula_1.2-2 jsonlite_1.5
- [19] nloptr_1.0.4 pbkrtest_0.4-7 annotate_1.54.0
- [22] broom_0.4.2 cluster_2.0.6 compiler_3.4.1
- [25] httr_1.2.1 backports_1.1.0 assertthat_0.2.0
- [28] Matrix_1.2-10 lazyeval_0.2.0 acepack_1.4.1
- [31] htmltools_0.3.6 quantreg_5.33 tools_3.4.1
- [34] gtable_0.2.0 glue_1.1.1 GenomeInfoDbData_0.99.0
- [37] reshape2_1.4.2 Rcpp_0.12.12 cellranger_1.1.0
- [40] psych_1.7.5 lme4_1.1-13 rvest_0.3.2
- [43] XML_3.98-1.9 MASS_7.3-47 zlibbioc_1.22.0
- [46] scales_0.4.1 hms_0.3 SparseM_1.77
- [49] yaml_2.1.14 memoise_1.1.0 gridExtra_2.2.1
- [52] rpart_4.1-11 RSQLite_2.0 latticeExtra_0.6-28
- [55] stringi_1.1.5 highr_0.6 checkmate_1.8.3
- [58] rlang_0.1.1 pkgconfig_2.0.1 bitops_1.0-6
- [61] evaluate_0.10.1 lattice_0.20-35 bindr_0.1
- [64] labeling_0.3 htmlwidgets_0.9 bit_1.1-12
- [67] bookdown_0.4 plyr_1.8.4 R6_2.2.2
- [70] Hmisc_4.0-3 DBI_0.7 haven_1.1.0
- [73] foreign_0.8-69 survival_2.41-3 RCurl_1.95-4.8
- [76] nnet_7.3-12 modelr_0.1.1 rmarkdown_1.6
- [79] locfit_1.5-9.1 grid_3.4.1 data.table_1.10.4
- [82] blob_1.1.0 digest_0.6.12 xtable_1.8-2
- [85] munsell_0.4.3 viridisLite_0.2.0
+ [1] backports_1.1.0 Hmisc_4.0-3 igraph_1.1.2
+ [4] plyr_1.8.4 lazyeval_0.2.0 shinydashboard_0.6.1
+ [7] splines_3.4.1 digest_0.6.12 htmltools_0.3.6
+ [10] viridis_0.4.0 checkmate_1.8.3 memoise_1.1.0
+ [13] cluster_2.0.6 annotate_1.54.0 modelr_0.1.1
+ [16] colorspace_1.3-2 blob_1.1.0 rvest_0.3.2
+ [19] ggrepel_0.6.5 haven_1.1.0 tximport_1.4.0
+ [22] RCurl_1.95-4.8 jsonlite_1.5 lme4_1.1-13
+ [25] bindr_0.1 zoo_1.8-0 survival_2.41-3
+ [28] glue_1.1.1 gtable_0.2.0 zlibbioc_1.22.0
+ [31] XVector_0.16.0 MatrixModels_0.4-1 SparseM_1.77
+ [34] scales_0.4.1 DBI_0.7 edgeR_3.18.1
+ [37] Rcpp_0.12.12 viridisLite_0.2.0 xtable_1.8-2
+ [40] htmlTable_1.9 foreign_0.8-69 bit_1.1-12
+ [43] Formula_1.2-2 DT_0.2 htmlwidgets_0.9
+ [46] httr_1.2.1 FNN_1.1 acepack_1.4.1
+ [49] pkgconfig_2.0.1 XML_3.98-1.9 nnet_7.3-12
+ [52] dynamicTreeCut_1.63-1 labeling_0.3 rlang_0.1.1
+ [55] reshape2_1.4.2 AnnotationDbi_1.38.2 munsell_0.4.3
+ [58] cellranger_1.1.0 tools_3.4.1 RSQLite_2.0
+ [61] evaluate_0.10.1 yaml_2.1.14 bit64_0.9-7
+ [64] mime_0.5 quantreg_5.33 xml2_1.1.1
+ [67] biomaRt_2.32.1 compiler_3.4.1 pbkrtest_0.4-7
+ [70] beeswarm_0.2.3 statmod_1.4.30 geneplotter_1.54.0
+ [73] stringi_1.1.5 lattice_0.20-35 Matrix_1.2-10
+ [76] psych_1.7.5 nloptr_1.0.4 data.table_1.10.4
+ [79] bitops_1.0-6 httpuv_1.3.5 R6_2.2.2
+ [82] latticeExtra_0.6-28 bookdown_0.4 gridExtra_2.2.1
+ [85] vipor_0.4.5 codetools_0.2-15 MASS_7.3-47
+ [88] assertthat_0.2.0 rhdf5_2.20.0 rjson_0.2.15
+ [91] rprojroot_1.2 mnormt_1.5-5 GenomeInfoDbData_0.99.0
+ [94] hms_0.3 grid_3.4.1 rpart_4.1-11
+ [97] minqa_1.2.4 rmarkdown_1.6 shiny_1.0.3
+ [100] lubridate_1.6.0 base64enc_0.1-3 ggbeeswarm_0.6.0
-LS0tCnRpdGxlOiAiVmlzdWFsIGV4cGxvcmF0aW9uIGZvciBiaW9pbmZvcm1hdGljcyIKYXV0aG9yOiAiQmVybmQgS2xhdXMiCmRhdGU6ICJgciBkb2NfZGF0ZSgpYCIKb3V0cHV0OiAKICAgIEJpb2NTdHlsZTo6aHRtbF9kb2N1bWVudDI6CiAgICAgICAgdG9jOiB0cnVlCiAgICAgICAgaGlnaGxpZ2h0OiB0YW5nbwogICAgICAgIHNlbGZfY29udGFpbmVkOiB0cnVlCiAgICAgICAgdG9jX2Zsb2F0OiBmYWxzZQogICAgICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKLS0tCgoKPCEtLTwKVG8gY29tcGlsZSB0aGlzIGRvY3VtZW50CmdyYXBoaWNzLm9mZigpO3JtKGxpc3Q9bHMoKSk7cm1hcmtkb3duOjpyZW5kZXIoJ2dyYXBoaWNzX2Jpb2luZi5SbWQnKTtwdXJsKCdncmFwaGljc19iaW9pbmYuUm1kJykKLS0+CgpgYGB7ciBvcHRpb25zLCBpbmNsdWRlPUZBTFNFfQpsaWJyYXJ5KGtuaXRyKQpvcHRpb25zKGRpZ2l0cz0zLCB3aWR0aD04MCkKZ29sZGVuX3JhdGlvIDwtICgxICsgc3FydCg1KSkgLyAyCm9wdHNfY2h1bmskc2V0KGVjaG89VFJVRSx0aWR5PUZBTFNFLGluY2x1ZGU9VFJVRSwKICAgICAgICAgICAgICAgZGV2PWMoJ3BuZycsICdwZGYnLCAnc3ZnJyksIGZpZy5oZWlnaHQgPSA1LCBmaWcud2lkdGggPSA0ICogZ29sZGVuX3JhdGlvLCBjb21tZW50ID0gJyAgJywgZHBpID0gMzAwLApjYWNoZSA9IFRSVUUpCmBgYAoKICoqTEFTVCBVUERBVEUgQVQqKgoKYGBge3IsIGVjaG89RkFMU0UsIGNhY2hlPUZBTFNFfQpwcmludChkYXRlKCkpCmBgYAoKCgojIFJlcXVpcmVkIHBhY2thZ2VzIGFuZCBvdGhlciBwcmVwYXJhdGlvbnMKCgpgYGB7ciByZXF1aXJlZF9wYWNrYWdlc19hbmRfZGF0YSwgZWNobyA9IFRSVUUsIGNhY2hlPUZBTFNFfQpsaWJyYXJ5KCJyZWFkeGwiKQpsaWJyYXJ5KCJCaW9jU3R5bGUiKQpsaWJyYXJ5KCJrbml0ciIpCmxpYnJhcnkoInRpZHl2ZXJzZSIpCmxpYnJhcnkoIlJDb2xvckJyZXdlciIpCmxpYnJhcnkoInN0cmluZ3IiKQpsaWJyYXJ5KCJwaGVhdG1hcCIpCmxpYnJhcnkoIm1hdHJpeFN0YXRzIikKbGlicmFyeSgicHVycnIiKQpsaWJyYXJ5KCJmZHJ0b29sIikKbGlicmFyeSgicmVhZHIiKQpsaWJyYXJ5KCJndG9vbHMiKQpsaWJyYXJ5KCJmYWN0b2V4dHJhIikKbGlicmFyeSgibWFncml0dHIiKQpsaWJyYXJ5KCJlbnRyb3B5IikKbGlicmFyeSgiZm9yY2F0cyIpCmxpYnJhcnkoInBsb3RseSIpCmxpYnJhcnkoImNvcnJwbG90IikKbGlicmFyeSgiY2FyIikKbGlicmFyeSgiZm9yY2F0cyIpCmxpYnJhcnkoIm9wZW54bHN4IikKbGlicmFyeSgicmVhZHhsIikKbGlicmFyeSgibGltbWEiKQpsaWJyYXJ5KCJTaW5nbGUubVRFQy5UcmFuc2NyaXB0b21lcyIpCmxpYnJhcnkoIkRFU2VxMiIpCmxpYnJhcnkoInRpYmJsZSIpCgp0aGVtZV9zZXQodGhlbWVfZ3JheShiYXNlX3NpemUgPSAxOCkpCgoKCmxpYnJhcnkoQmlvY1BhcmFsbGVsKQptdWx0aWNvcmVQYXJhbSA8LSBNdWx0aWNvcmVQYXJhbSh3b3JrZXJzID0gcm91bmQocGFyYWxsZWw6OmRldGVjdENvcmVzKCkgKiAwLjUpKQpyZWdpc3RlcihtdWx0aWNvcmVQYXJhbSkKCmRhdGFfZGlyIDwtIGZpbGUucGF0aCgiZGF0YS8iKQoKYGBgCgoKCgpgYGB7ciBpbXBvcnRfZ2VuZV9leHByZXNzaW9uLCBlY2hvPUZBTFNFLCBldmFsPUZBTFNFfQojIEhlcmUgd2UgaW1wb3J0IHRoZSBnZW5lIGV4cHJlc3Npb24gZGF0YSB1c2luZyBvbmx5IHRoZSBzdWJzZXQgb2YgaGlnaGx5IAojIHZhcmlhYmxlIGdlbmVzLCByZXNhdmUgaXQgCmxvYWQoZmlsZS5wYXRoKGRhdGFfZGlyLCAiZGVHZW5lc05vbmUuUkRhdGEiKSkKbG9hZChmaWxlLnBhdGgoZGF0YV9kaXIsICJtVEVDZHhkLlJEYXRhIikpCgptdGVjX2NvdW50cyA8LSBjb3VudHMoZHhkKVtkZUdlbmVzTm9uZSwgXQoKbXRlY19jb3VudHMgPC0gYXNfdGliYmxlKHJvd25hbWVzX3RvX2NvbHVtbihhcy5kYXRhLmZyYW1lKG10ZWNfY291bnRzKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXIgPSAiZW5zZW1ibF9pZCIpKQoKbXRlY19jZWxsX2Fubm8gPC0gYXNfdGliYmxlKGFzLmRhdGEuZnJhbWUoY29sRGF0YShkeGQpKSkgJT4lCiAgICAgICAgICAgICAgICAgIG1vZGlmeV9pZigucCA9IGlzLmZhY3RvciwgYXMuY2hhcmFjdGVyKQoKZGF0YSgiYmlvdHlwZXMiKQpkYXRhKCJnZW5lTmFtZXMiKQoKbXRlY19nZW5lX2Fubm8gPC0gdGliYmxlKGJpb3R5cGUpICU+JQogICAgICAgICAgICAgICAgICBhZGRfY29sdW1uKGVuc2VtYmxfaWQgPSBuYW1lcyhiaW90eXBlKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZW5lX25hbWUgPSBnZW5lTmFtZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLmJlZm9yZSA9ICAiYmlvdHlwZSIpCgptdGVjX2NlbGxfYW5ubyA8LSBjb2xEYXRhKGR4ZCkKCnNhdmUobXRlY19jb3VudHMsIGZpbGUgPSBmaWxlLnBhdGgoZGF0YV9kaXIsICJtdGVjX2NvdW50cy5SRGF0YSIpLAogICAgIGNvbXByZXNzID0gInh6IikKCnNhdmUobXRlY19jZWxsX2Fubm8sIGZpbGUgPSBmaWxlLnBhdGgoZGF0YV9kaXIsICJtdGVjX2NlbGxfYW5uby5SRGF0YSIpLAogICAgIGNvbXByZXNzID0gInh6IikKCgpzYXZlKG10ZWNfZ2VuZV9hbm5vLCBmaWxlID0gZmlsZS5wYXRoKGRhdGFfZGlyLCAibXRlY19nZW5lX2Fubm8uUkRhdGEiKSwKICAgICBjb21wcmVzcyA9ICJ4eiIpCgp0cmFzIDwtIGFzX3RpYmJsZSh0cmFzKQoKc2F2ZSh0cmFzLCBmaWxlID0gZmlsZS5wYXRoKGRhdGFfZGlyLCAidHJhcy5SRGF0YSIpLAogICAgIGNvbXByZXNzID0gInh6IikKYGBgCgoKIyBtVEVDIHNpbmdsZSBjZWxsLVJOQS1TZXEgZGF0YQoKCldlIHN1bW1hcml6ZSBzaW5nbGUtY2VsbCB0cmFuc2NyaXB0b21lcyBpbiB0aGUgZm9ybSBvZiBjb3VudCBtYXRyaWNlcyBpbiB3aGljaCBlYWNoIHJvdwpyZXByZXNlbnRzIGEgZ2VuZSBhbmQgZWFjaCBjb2x1bW4gcmVwcmVzZW50cyBvbmUgY2VsbC4gVGhlIG1hdHJpeCBpcyBmaWxsZWQKd2l0aCB0aGUgbnVtYmVyIG9mIHNlcXVlbmNlZCBmcmFnbWVudHMgd2hvc2UgZ2Vub21pYyBhbGlnbm1lbnQgb3ZlcmxhcHMgd2l0aCB0aGUKZ2Vub21pYyBjb29yZGluYXRlcyBvZiB0aGUgZ2VuZXMuIEZvciB0aGlzLCB3ZSBvbmx5IHVzZSBjZWxscyBmb3Igd2hpY2ggbW9yZSAKdGhhbiA0MCUgb2YgdGhlIHNlcXVlbmNlZCBmcmFnbWVudHMgY291bGQgYmUgdW5pcXVlbHkgYXNzaWduZWQgdG8gdGhlIE1vdXNlIApyZWZlcmVuY2UgZ2Vub21lLiBXZSBhbHNvIGRpc2NhcmRlZCAyOCBjZWxscyBmcm9tIHRoZSBiYXRjaCA0IChzaW5jZSB0aGV5IHdlcmUgCmZyb20gYW5vdGhlciBjZWxsIHR5cGUgdGhhdCB3YXMgc2VxdWVuY2VkIHRvZ2V0aGVyIHdpdGggc29tZSBvZiB0aGUgbVRFQ3MpLiAKCkl0IGlzIHNlbnNpYmxlIHRvIG9ubHkgdXNlIGhpZ2hseSB2YXJpYWJsZSBnZW5lcyBpbiB0aGUgYW5hbHlzaXMgb2YgCnNpbmdsZSBjZWxsIGRhdGEgKEhWRyksIHRoZSBvcmlnaW5hbCBwdWJsaWNhdGlvbiBpZGVudGlmaWVkICBtb3JlIHRoYW4KOSwwMDAgYXMgaGlnaGx5IHZhcmlhYmxlIHVzaW5nIGEgbWV0aG9kIHB1Ymxpc2hlZCBpbiBCcmVubmVja2UgZXQuIGFsLiAyMDEzLgpXZSByZXRhaW4gdGhvc2UgZ2VuZXMgZm9yIGZ1cnRoZXIgYW5hbHlzaXMuCgpXZSBhbHNvIGltcG9ydCBhIHRhYmxlIHdpdGggZ2VuZXMgYW5ub3RhdGVkIGFzIHRpc3N1ZSByZXN0cmljdGVkIGFudGlnZW5zICh0cmFzKQoKZnJvbSBTaGludG8gZXQuIGFsLiAyMDEzIGFuZCBhbm5vdGF0aW9uIGZvciBtb3VzZSBnZW5lcyBmcm9tIEVOU0VNQkwuCgoKYGBge3IgaW1wb3J0X2RhdGF9CmxvYWQoZmlsZS5wYXRoKGRhdGFfZGlyLCAibXRlY19jb3VudHMuUkRhdGEiKSkKbG9hZChmaWxlLnBhdGgoZGF0YV9kaXIsICJtdGVjX2NlbGxfYW5uby5SRGF0YSIpKQpsb2FkKGZpbGUucGF0aChkYXRhX2RpciwgIm10ZWNfZ2VuZV9hbm5vLlJEYXRhIikpCmxvYWQoZmlsZS5wYXRoKGRhdGFfZGlyLCAidHJhcy5SRGF0YSIpKQpgYGAKCgojIEdyYXBoaWNzIGluIFIKCiMgZ2dwbG90MnsjZ2d9CgpgciBDUkFOcGtnKCJnZ3Bsb3QyIilgIGlzIGEgcGFja2FnZSBieSBIYWRsZXkgV2lja2hhbSAgdGhhdCBpbXBsZW1lbnRzIHRoZSBpZGVhIG9mIAoqZ3JhbW1hciBvZiBncmFwaGljcyogLS0gYSBjb25jZXB0IGNyZWF0ZWQgYnkgTGVsYW5kIFdpbGtpbnNvbiBpbiBoaXMgYm9vayBvZiB0aGUgc2FtZSBuYW1lLiBDb21wcmVoZW5zaXZlIGRvY3VtZW50YXRpb24gZm9yIHRoZSBwYWNrYWdlIGNhbgpiZSBmb3VuZCBvbiA8aHR0cDovL2dncGxvdDIudGlkeXZlcnNlLm9yZy8+LiAgVGhlIG9ubGluZSBkb2N1bWVudGF0aW9uIGluY2x1ZGVzCmV4YW1wbGUgdXNlIGNhc2VzIGZvciBlYWNoIG9mIHRoZSBncmFwaGljIHR5cGVzIHRoYXQgYXJlIGludHJvZHVjZWQgaW4gdGhpcyBsYWIgKGFuZAptYW55IG1vcmUpIGFuZCBpcyBhbiBpbnZhbHVhYmxlIHJlc291cmNlIHdoZW4gY3JlYXRpbmcgZmlndXJlcy4KClNpbmdsZSBjZWxsIFJOQS1TZXEgZGF0YSBoYXMgbWFueSB6ZXJvIGR1ZSB0byBhIGxpbWl0ZWQgY2FwdHVyZSBlZmZpY2llbmN5IAphbmQgYmlhc2VzIGluIHRoZSByZXZlcnNlIHRyYW5zY3JpcHRpb24uIEl0IGlzIHRodXMgaW50ZXJlc3RpbmcgdG8gc2VlIGhvdwp0aGUgdG90YWwgbnVtYmVyIG9mIGdlbmVzIGV4cHJlc3NlZCAoYXQgbGVhc3Qgb25lIGNvdW50KSByZWxhdGVzIHRvIAp0aGUgbnVtYmVyIG9mIFRSQSBlbmNvZGluZyBnZW5lcyBleHByZXNzZWQuIAoKVGhlIHNpbmdsZSBjZWxsIGRhdGEgYWxzbyBjb250YWlucyBtVEVDIGNlbGxzIHNlbGVjdCBiYXNlZCBvbiBzdXJmYWNlIG1hcmtlcnMsCndlIGV4Y2x1ZGUgdGhvc2UgYW5kIG1ha2Ugc3VyZSB0byBpbnNwZWN0IG9ubHkgcHJvdGVpbiBjb2RpbmcgZ2VuZXMuCkluIG9yZGVyIHRvIGRvIHRoaXMsIHdlIGZpcnN0IHR1cm4gdGhlIGNvdW50IHRhYmxlOgoKYGBge3IgbXRlY19jb3VudF90YWJsZX0KbXRlY19jb3VudHMKYGBgCgp3aGljaCBjb250YWlucyB0aGUgY291bnRzIGZvciBlYWNoIGNlbGwgaW4gc2VwYXJ0ZSBjb2x1bW5zIGludG8gYSB0aWR5IApkYXRhIGZyYW1lLiBXZSBgZ2F0aGVyYCBhbGwgY29sdW1ucyBleGNlcHQgZm9yIHRoZSBmaXJzdCBvbmUgYW5kIHRoZW4KYWRkIGEgY29sdW1uIHRlbGxpbmcgdXMgd2hldGhlciBhIHNwZWNpZmljIGdlbmVzIGlzIGNvZGluZyBmb3IgYSBUUkEKYW5kIHdoZXRoZXIgaXQgd2FzIGRldGVjdGVkIG9yIG5vdC4KClRoZW4sIHdlIGpvaW4gdGhlIGFubm90YXRpb24gdG8gdGhlIHRpZHkgdGFibGUKCmBgYHtyIHRpZHlfY291bnR9Cm10ZWNfY291bnRzX3RpZHkgPC0gZ2F0aGVyKG10ZWNfY291bnRzLCBrZXkgPSAiY2VsbF9pZCIsIHZhbHVlID0gImNvdW50IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgLWVuc2VtYmxfaWQpICU+JQogICAgICAgICAgICAgICAgICAgICAgICAgICBtdXRhdGUoaXNfdHJhID0gZW5zZW1ibF9pZCAlaW4lIHRyYXMkZ2VuZS5pZHMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpc19kZXRlY3RlZCA9IGNvdW50ID4gMCkgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGxlZnRfam9pbihtdGVjX2NlbGxfYW5ubywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gYygiY2VsbF9pZCIgPSAiY2VsbElEIikpCmBgYAoKTm93IHdlIGNhbiBjb21wdXRlIHRoZSBudW1iZXIgb2YgVFJBIGdlbmVzIGRldGVjdGVkIHBlciBjZWxsIGFuZCBmaWx0ZXIgCnRoZSBub24tLXByb3RlaW4gY29kaW5nIGdlbmVzIGFzIHdlbGwgYXMgdGhlIGNlbGxzIHRoYXQgaGF2ZSBiZWVuIHByb2Nlc3NlZAp1c2luZyBGQUNTIGJhc2VkIG9uIGEgc3VyZmFjZSBtYXJrZXIuCgpGaW5hbGx5LCB3ZSBvYnRhaW4gYSB0aWR5IHRhYmxlIHdpdGggdHdvIGNvbHVtbnMgcGVyIGNlbGw6IHRoZSBudW1iZXIgb2YgCnRyYXMgYW5kIG5vbi10cmFzIGV4cHJlc3NlZC4gVGhlcmUgaXMgYSBzdWJ0bGUgY2hhbmdlIGluICBvYnNlcnZhdGlvbnM6IAp3ZSBnbyBmcm9tIGdlbmVzIHdpdGhpbiBhIHNpbmdsZSBjZWxsIGFzIG91ciB1bml0IHRvIHNpbmdsZSBjZWxscy4KCmBgYHtyIHRyYV9wZXJfY2VsbH0KCnRyYV9kZXRlY3RlZCA8LSBmaWx0ZXIobXRlY19jb3VudHNfdGlkeSwgaXNfZGV0ZWN0ZWQgPT0gVFJVRSwgCiAgICAgICAgICAgICAgICAgICAgICAgU3VyZmFjZU1hcmtlciA9PSAiTm9uZSIpICU+JQogICAgICAgICAgICAgICAgbXV0YXRlKGlzX3RyYSA9IGlmZWxzZShpc190cmEsICJ0cmEiLCAibm90X2FfdHJhIikpICU+JQogICAgICAgICAgICAgICAgZ3JvdXBfYnkoY2VsbF9pZCwgaXNfdHJhKSAlPiUgCiAgICAgICAgICAgICAgICB0YWxseSgpICU+JQogICAgICAgICAgICAgICAgc3ByZWFkKGtleSA9IGlzX3RyYSwgdmFsdWUgPSBuKSAlPiUKICAgICAgICAgICAgICAgIG11dGF0ZSh0b3RhbF9kZXRlY3RlZCA9IHN1bSh0cmEsIG5vdF9hX3RyYSkpCgp0cmFfZGV0ZWN0ZWQKYGBgCgoKV2UgdmlzdWFsaXplIHRoaXMgcmVsYXRpb25zaGlwIGluIGEgc2NhdHRlcnBsb3Qgd2l0aCBnZ3Bsb3QyLgoKYGBge3IgdHJhdnNhbGwsIGZpZy5jYXA9IlRvdGFsIG51bWJlciBvZiBnZW5lcyB2cyBUUkEifQpzY2F0dGVyX3RyYSA8LSBnZ3Bsb3QodHJhX2RldGVjdGVkLCBhZXMoeCA9IHRvdGFsX2RldGVjdGVkLCB5ID0gdHJhKSkrIAogICAgICAgICAgICAgICBnZW9tX3BvaW50KCkgKwogICAgICAgICAgICAgICBjb29yZF9lcXVhbCgpCgpzY2F0dGVyX3RyYQpgYGAKCgpXZSBqdXN0IHdyb3RlIG91ciBmaXJzdCAic2VudGVuY2UiIHVzaW5nIHRoZSBncmFtbWFyIG9mIGdyYXBoaWNzLiAKTGV0IHVzIGRlY29uc3RydWN0IHRoaXMgc2VudGVuY2UuCkZpcnN0LCB3ZSBzcGVjaWZpZWQgdGhlIGRhdGFmcmFtZSB0aGF0IGNvbnRhaW5zIHRoZSBkYXRhLCBgbXRlY19jb3VudHNfdGlkeWAuClRoZW4gd2UgdG9sZCBgZ2dwbG90YCB2aWEgdGhlIGBhZXNgLiBUaGlzIHN0YW5kcyBmb3IgYGFlc3RoZXRpY2AgYXJndW1lbnQgCihBZXN0aGV0aWNzIGFyZSB2aXN1YWwgcHJvcGVydHkgb2YgdGhlIG9iamVjdHMgaW4geW91ciBwbG90LikKYW5kIHRlbGxzIGByIENSQU5wa2coImdncGxvdDIiKWAgd2hpY2ggdmFyaWFibGVzIAp3ZSB3YW50IG9uIHRoZSBcKHhcKS0tIGFuZCBcKHlcKS0tYXhlcywgcmVzcGVjdGl2ZWx5LiAKCkZpbmFsbHksIHdlIHN0YXRlZCB0aGF0IHdlIHdhbnQgdGhlIHBsb3QgdG8gdXNlIHBvaW50cywgYnkgYWRkaW5nIHRoZSByZXN1bHQgCm9mIGNhbGxpbmcgdGhlIGZ1bmN0aW9uIGBnZW9tX3BvaW50YC4gVGhlIHBsb3Qgd291bGQgYmUgZ29vZCB0byBnbyBub3csIGJ1dAphcyBib3RoIGF4ZXMgcmVwcmVzZW50IGNvdW50cyBhbmQgdGh1cyBoYXZlIHRoZSBzYW1lIHVuaXRzLCAKd2Ugd2FudCB0aGVpciBhc3BlY3QgcmF0aW8gdG8gYmUgZXF1YWwuIAoKTm90ZSB0aGF0IGluIGByIENSQU5wa2coImdncGxvdDIiKWAsIHdlIGJ1aWxkIGEgcGxvdCAibGF5ZXIgYnkgbGF5ZXIiOgpXZSBmaXJzdCBzcGVjaWZ5IGEgbWFwcGluZyBmcm9tIHRoZSBkYXRhIHRvIGFlc3RoZXRpY3MgYW5kIHRoZW4gdXNlIGFuCmFwcHJvcHJpYXRlIGdlb21ldHJ5IHRvIGRpc3BsYXkgaXQuIFdlIGNhbiBhbHdheXMgYWRkIG5ldyBsYXllcnMgb3IgbW9kaWZ5CmV4aXN0aW5nIG9uZXMgYnkgbGl0ZXJhbGx5IGFkZGluZyB0aGVtLCBoZXJlIHdlIGFkZCBob3Jpem9udGFsIGxpbmVzCmluZGljYXRpbmcgb3VyIG1hcmdpbmFsIGNvdW50cyB0byB0aGUgc2NhdHRlciBwbG90LiBUaGlzIGlzIGtub3duIAphcyBhICJydWcgcGxvdCIKCmBgYHtyIG1vcmVfbGF5ZXJzfQpzY2F0dGVyX3RyYSArIAogIGdlb21fcnVnKGFscGhhID0gSSgwLjIpKQpgYGAKCldlIHNldCAoaW5zdGVhZCBvZiBtYXBwZWQpIHRoZSBhbHBoYSB2YWx1ZSB0byAwLjIsIHRoaXMgaW5jcmVhc2VzZXMgdGhlIAp0cmFuc3BhcmFuY3kgb2YgdGhlIHJ1Z3MgYW5kIGFsbGV2aWF0ZXMgb3ZlcnBsb3R0aW5nLgoKCiMjIEV4ZXJjaXNlOiBHZW9tZXRyaWVzIGFuZCBhc3BlY3QgcmF0aW9zCgoxLiBXaGF0IGhhcHBlbnMgaWYgeW91IG9taXQgdGhlIGNhbGxzIHRvIGBnZW9tX3BvaW50KClgIGFuZAogICBgY29vcmRfZXF1YWwoKWAgPwogICAKMi4gVXNlIHRoZSBpbnRlcm5ldCAoZS5nLiA8aHR0cDovL2dncGxvdDIudGlkeXZlcnNlLm9yZy8+KSB0byBmaW5kIApvdXQgaG93IHRvIGFkZCBhIHNtb290aGluZyBjdXJ2ZSB0byB0aGUgcGxvdC4gQ2FuIHlvdSBjaGFuZ2UgdGhlIHNtb290aGVyCnRvIGEgcmVncmVzc2lvbiBsaW5lPwoKCmBgYHtyIGV4X2dlb219CmdncGxvdCh0cmFfZGV0ZWN0ZWQsIGFlcyh4ID0gdG90YWxfZGV0ZWN0ZWQsIHkgPSB0cmEpKQoKc2NhdHRlcl90cmEgKwogIGdlb21fc21vb3RoKGNvbG9yID0gImNvcmFsMyIpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iKQpgYGAKCkFzIEZpZ3VyZSBcQHJlZihmaWc6dHJhdnNhbGwpIHNob3dzIHRoZSB0d28gbnVtYmVycyBzZWVtIHZlcnkgY29ycmVsYXRlZCAKYW5kIHNlZW0gdG8gZm9sbG93IGEgbGluZWFyIHJlbGF0aW9uLCAKaW5kaWNhdGluZyB0aGF0IHRoZSBudW1iZXIgb2YgVFJBIGdlbmVzIHRoYXQgYXJlIGRldGVjdGVkIHdpdGhpbiBhIGNlbGwgCmlzIHByb3BvcnRpb25hbCB0byB0aGUgbnVtYmVyIG9mIGRldGVjdGVkIGdlbmVzLiBSZWdyZXNzaW9uIG1vZGVscyAKYWxsb3cgZm9yIHRoZSBzeXN0ZW1hdGljIGNoYXJhY3Rlcml6YXRpb24gb2Ygc3VjaCByZWxhdGlvbnNoaXBzLiAKCiMgUmVncmVzc2lvbiBtb2RlbHMKCkluIHJlZ3Jlc3Npb24gd2UgdXNlIG9uZSB2YXJpYWJsZSB0byBleHBsYWluIG9yIHByZWRpY3QgdGhlIG90aGVyLiBJdCBpcyBjdXN0b21hcnkKdG8gcGxvdCB0aGUgcHJlZGljdG9yIHZhcmlhYmxlIG9uIHRoZSB4LS1heGlzIGFuZCB0aGUgcHJlZGljdGVkIHZhcmlhYmxlIG9uIHRoZQp5LS1heGlzLgpUaGUgcHJlZGljdG9yIGlzIGFsc28gY2FsbGVkIHRoZSBpbmRlcGVuZGVudCB2YXJpYWJsZSwgdGhlIGV4cGxhbmF0b3J5IHZhcmlhYmxlLAp0aGUgY292YXJpYXRlLCBvciBzaW1wbHkgeC4gVGhlIHByZWRpY3RlZCB2YXJpYWJsZSBpcyBjYWxsZWQgdGhlIGRlcGVuZGVudCAKdmFyaWFibGUsIG9yIHNpbXBseSB5LgoKSW4gYSByZWdyZXNzaW9uIHByb2JsZW0gdGhlIGRhdGEgYXJlIHBhaXJzIFwoKHhfaSAsIHlfaSApXCkgZm9yIFwoaSA9IDEsIFxkb3RzYyAsIG5cKS4KRm9yIGVhY2ggIFwoeF9pIFwpLCBcKHlfaVwpIGlzIGEgcmFuZG9tIHZhcmlhYmxlIHdob3NlIGRpc3RyaWJ1dGlvbiBkZXBlbmRzIG9uICBcKHhfaSBcKS4gClRoZSByZWdyZXNzaW9uIG1vZGVsIGlzOgoKXFsKeV9pID0gZyh4X2kpICsgXHZhcmVwc2lsb25faSAuClxdCgpUaGUgYWJvdmUgIGV4cHJlc3NlcyBcKHlfaVwpIGFzIGEgc3lzdGVtYXRpYyBwYXJ0IFwoZyh4X2kpXCkKYW5kIGFuIHVuZXhwbGFpbmVkIHBhcnQKJFx2YXJlcHNpbG9uX2kkLiBPciBtb3JlIGluZm9ybWFsbHk6IAoKX19yZXNwb25zZSA9IHNpZ25hbCArIG5vaXNlX18KClwoZ1wpIGlzIGNhbGxlZCB0aGUgcmVncmVzc2lvbiBmdW5jdGlvbiwgYW5kIHJlZ3Jlc3Npb24gbW9kZWxzIHByb3ZpZGUgbWVhbnMKdG8gY29tcHV0ZSBcKGdcKS4gQSBjb21tb24gY2hvaWNlIGZvciBcKGdcKSBpcyBhIGxpbmVhciBmdW5jdGlvbiwgaWYgd2UgaGF2ZSBvbmx5CnNpbmdsZSBwcmVkaWN0b3IgJFgkLCB0aGUgc2ltcGxlIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsIGlzOgoKXFsKeV9pID0gYSArICBiIHhfe2l9ICsgXHZhcmVwc2lsb25faTsgXHF1YWQKIFx2YXJlcHNpbG9uX2kgXHNpbSBOKDAsIFxzaWdtYSk7ClxdCgpXZSBjYW4gb2YgY291cnNlIGFsd2F5cyBhZGQgbW9yZSBwcmVkaWN0b3JzIHRvIHRoZSBsaW5lYXIgZnVuY3Rpb24uIFRoZSBjb2VmZmljaWVudApcKGJcKSBpcyBjYWxsZWQgdGhlIF9fc2xvcGVfXyBhbmQgXChhXCkgaXMgY2FsbGVkIHRoZSBfX2ludGVyY2VwdF9fIC4KCldlIGNhbiBmaXQgYSBsaW5lYXIgcmVncmVzc2lvbiB2aWEgYSBjYWxsIHRvIHRoZSBmdW5jdGlvbiBgbG0oKWAuIFRoZSByZWdyZXNzaW9uCm1vZGVsIGlzIHNwZWNpZmllZCB1c2luZyBSJ3MgZm9ybXVsYSBub3RhdGlvbi4KCmBgYHtyIHJlZ3Jlc3NzaW9uX3RyYX0KbG1fdHJhIDwtIGxtKHRyYSB+IHRvdGFsX2RldGVjdGVkLCBkYXRhID0gdHJhX2RldGVjdGVkKQpsbV90cmEKYGBgCgoKIyBTZXNzaW9uIEluZm8KCmBgYHtyIHNlc3Npb25faW5mbywgY2FjaGUgPSBGQUxTRX0Kc2Vzc2lvbkluZm8oKQpgYGAKCgoKCg==
+LS0tCnRpdGxlOiAiVmlzdWFsIGV4cGxvcmF0aW9uIGZvciBiaW9pbmZvcm1hdGljcyIKYXV0aG9yOiAiQmVybmQgS2xhdXMiCmRhdGU6ICJgciBkb2NfZGF0ZSgpYCIKb3V0cHV0OiAKICAgIEJpb2NTdHlsZTo6aHRtbF9kb2N1bWVudDI6CiAgICAgICAgdG9jOiB0cnVlCiAgICAgICAgaGlnaGxpZ2h0OiB0YW5nbwogICAgICAgIHNlbGZfY29udGFpbmVkOiB0cnVlCiAgICAgICAgdG9jX2Zsb2F0OiBmYWxzZQogICAgICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKLS0tCgoKPCEtLTwKVG8gY29tcGlsZSB0aGlzIGRvY3VtZW50CmdyYXBoaWNzLm9mZigpO3JtKGxpc3Q9bHMoKSk7cm1hcmtkb3duOjpyZW5kZXIoJ2dyYXBoaWNzX2Jpb2luZi5SbWQnKTtwdXJsKCdncmFwaGljc19iaW9pbmYuUm1kJykKLS0+CgpgYGB7ciBvcHRpb25zLCBpbmNsdWRlPUZBTFNFfQpsaWJyYXJ5KGtuaXRyKQpvcHRpb25zKGRpZ2l0cz0zLCB3aWR0aD04MCkKZ29sZGVuX3JhdGlvIDwtICgxICsgc3FydCg1KSkgLyAyCm9wdHNfY2h1bmskc2V0KGVjaG89VFJVRSx0aWR5PUZBTFNFLGluY2x1ZGU9VFJVRSwKICAgICAgICAgICAgICAgZGV2PWMoJ3BuZycsICdwZGYnLCAnc3ZnJyksIGZpZy5oZWlnaHQgPSA1LCBmaWcud2lkdGggPSA0ICogZ29sZGVuX3JhdGlvLCBjb21tZW50ID0gJyAgJywgZHBpID0gMzAwLApjYWNoZSA9IFRSVUUpCmBgYAoKICoqTEFTVCBVUERBVEUgQVQqKgoKYGBge3IsIGVjaG89RkFMU0UsIGNhY2hlPUZBTFNFfQpwcmludChkYXRlKCkpCmBgYAoKCgojIFJlcXVpcmVkIHBhY2thZ2VzIGFuZCBvdGhlciBwcmVwYXJhdGlvbnMKCgpgYGB7ciByZXF1aXJlZF9wYWNrYWdlc19hbmRfZGF0YSwgZWNobyA9IFRSVUUsIGNhY2hlPUZBTFNFfQpsaWJyYXJ5KCJyZWFkeGwiKQpsaWJyYXJ5KCJCaW9jU3R5bGUiKQpsaWJyYXJ5KCJrbml0ciIpCmxpYnJhcnkoInRpZHl2ZXJzZSIpCmxpYnJhcnkoIlJDb2xvckJyZXdlciIpCmxpYnJhcnkoInN0cmluZ3IiKQpsaWJyYXJ5KCJwaGVhdG1hcCIpCmxpYnJhcnkoIm1hdHJpeFN0YXRzIikKbGlicmFyeSgicHVycnIiKQpsaWJyYXJ5KCJmZHJ0b29sIikKbGlicmFyeSgicmVhZHIiKQpsaWJyYXJ5KCJndG9vbHMiKQpsaWJyYXJ5KCJmYWN0b2V4dHJhIikKbGlicmFyeSgibWFncml0dHIiKQpsaWJyYXJ5KCJlbnRyb3B5IikKbGlicmFyeSgiZm9yY2F0cyIpCmxpYnJhcnkoInBsb3RseSIpCmxpYnJhcnkoImNvcnJwbG90IikKbGlicmFyeSgiY2FyIikKbGlicmFyeSgiZm9yY2F0cyIpCmxpYnJhcnkoIm9wZW54bHN4IikKbGlicmFyeSgicmVhZHhsIikKbGlicmFyeSgibGltbWEiKQpsaWJyYXJ5KCJTaW5nbGUubVRFQy5UcmFuc2NyaXB0b21lcyIpCmxpYnJhcnkoIkRFU2VxMiIpCmxpYnJhcnkoInRpYmJsZSIpCmxpYnJhcnkoImJyb29tIikKbGlicmFyeSgic2NyYW4iKQpsaWJyYXJ5KCJsb2NmaXQiKQoKdGhlbWVfc2V0KHRoZW1lX2dyYXkoYmFzZV9zaXplID0gMTgpKQoKCgpsaWJyYXJ5KEJpb2NQYXJhbGxlbCkKbXVsdGljb3JlUGFyYW0gPC0gTXVsdGljb3JlUGFyYW0od29ya2VycyA9IHJvdW5kKHBhcmFsbGVsOjpkZXRlY3RDb3JlcygpICogMC41KSkKcmVnaXN0ZXIobXVsdGljb3JlUGFyYW0pCgpkYXRhX2RpciA8LSBmaWxlLnBhdGgoImRhdGEvIikKCmBgYAoKCgoKYGBge3IgaW1wb3J0X2dlbmVfZXhwcmVzc2lvbiwgZWNobz1GQUxTRSwgZXZhbD1GQUxTRX0KIyBIZXJlIHdlIGltcG9ydCB0aGUgZ2VuZSBleHByZXNzaW9uIGRhdGEgdXNpbmcgb25seSB0aGUgc3Vic2V0IG9mIGhpZ2hseSAKIyB2YXJpYWJsZSBnZW5lcywgcmVzYXZlIGl0IApsb2FkKGZpbGUucGF0aChkYXRhX2RpciwgImRlR2VuZXNOb25lLlJEYXRhIikpCmxvYWQoZmlsZS5wYXRoKGRhdGFfZGlyLCAibVRFQ2R4ZC5SRGF0YSIpKQoKbXRlY19jb3VudHMgPC0gY291bnRzKGR4ZClbZGVHZW5lc05vbmUsIF0KCm10ZWNfY291bnRzIDwtIGFzX3RpYmJsZShyb3duYW1lc190b19jb2x1bW4oYXMuZGF0YS5mcmFtZShtdGVjX2NvdW50cyksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyID0gImVuc2VtYmxfaWQiKSkKCm10ZWNfY2VsbF9hbm5vIDwtIGFzX3RpYmJsZShhcy5kYXRhLmZyYW1lKGNvbERhdGEoZHhkKSkpICU+JQogICAgICAgICAgICAgICAgICBtb2RpZnlfaWYoLnAgPSBpcy5mYWN0b3IsIGFzLmNoYXJhY3RlcikKCmRhdGEoImJpb3R5cGVzIikKZGF0YSgiZ2VuZU5hbWVzIikKCm10ZWNfZ2VuZV9hbm5vIDwtIHRpYmJsZShiaW90eXBlKSAlPiUKICAgICAgICAgICAgICAgICAgYWRkX2NvbHVtbihlbnNlbWJsX2lkID0gbmFtZXMoYmlvdHlwZSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2VuZV9uYW1lID0gZ2VuZU5hbWVzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIC5iZWZvcmUgPSAgImJpb3R5cGUiKQoKbXRlY19jZWxsX2Fubm8gPC0gY29sRGF0YShkeGQpCgpzYXZlKG10ZWNfY291bnRzLCBmaWxlID0gZmlsZS5wYXRoKGRhdGFfZGlyLCAibXRlY19jb3VudHMuUkRhdGEiKSwKICAgICBjb21wcmVzcyA9ICJ4eiIpCgpzYXZlKG10ZWNfY2VsbF9hbm5vLCBmaWxlID0gZmlsZS5wYXRoKGRhdGFfZGlyLCAibXRlY19jZWxsX2Fubm8uUkRhdGEiKSwKICAgICBjb21wcmVzcyA9ICJ4eiIpCgoKc2F2ZShtdGVjX2dlbmVfYW5ubywgZmlsZSA9IGZpbGUucGF0aChkYXRhX2RpciwgIm10ZWNfZ2VuZV9hbm5vLlJEYXRhIiksCiAgICAgY29tcHJlc3MgPSAieHoiKQoKdHJhcyA8LSBhc190aWJibGUodHJhcykKCnNhdmUodHJhcywgZmlsZSA9IGZpbGUucGF0aChkYXRhX2RpciwgInRyYXMuUkRhdGEiKSwKICAgICBjb21wcmVzcyA9ICJ4eiIpCmBgYAoKCiMgbVRFQyBzaW5nbGUgY2VsbC1STkEtU2VxIGRhdGEKCgpXZSBzdW1tYXJpemUgc2luZ2xlLWNlbGwgdHJhbnNjcmlwdG9tZXMgaW4gdGhlIGZvcm0gb2YgY291bnQgbWF0cmljZXMgaW4gd2hpY2ggZWFjaCByb3cKcmVwcmVzZW50cyBhIGdlbmUgYW5kIGVhY2ggY29sdW1uIHJlcHJlc2VudHMgb25lIGNlbGwuIFRoZSBtYXRyaXggaXMgZmlsbGVkCndpdGggdGhlIG51bWJlciBvZiBzZXF1ZW5jZWQgZnJhZ21lbnRzIHdob3NlIGdlbm9taWMgYWxpZ25tZW50IG92ZXJsYXBzIHdpdGggdGhlCmdlbm9taWMgY29vcmRpbmF0ZXMgb2YgdGhlIGdlbmVzLiBGb3IgdGhpcywgd2Ugb25seSB1c2UgY2VsbHMgZm9yIHdoaWNoIG1vcmUgCnRoYW4gNDAlIG9mIHRoZSBzZXF1ZW5jZWQgZnJhZ21lbnRzIGNvdWxkIGJlIHVuaXF1ZWx5IGFzc2lnbmVkIHRvIHRoZSBNb3VzZSAKcmVmZXJlbmNlIGdlbm9tZS4gV2UgYWxzbyBkaXNjYXJkZWQgMjggY2VsbHMgZnJvbSB0aGUgYmF0Y2ggNCAoc2luY2UgdGhleSB3ZXJlIApmcm9tIGFub3RoZXIgY2VsbCB0eXBlIHRoYXQgd2FzIHNlcXVlbmNlZCB0b2dldGhlciB3aXRoIHNvbWUgb2YgdGhlIG1URUNzKS4gCgpJdCBpcyBzZW5zaWJsZSB0byBvbmx5IHVzZSBoaWdobHkgdmFyaWFibGUgZ2VuZXMgaW4gdGhlIGFuYWx5c2lzIG9mIApzaW5nbGUgY2VsbCBkYXRhIChIVkcpLCB0aGUgb3JpZ2luYWwgcHVibGljYXRpb24gaWRlbnRpZmllZCAgbW9yZSB0aGFuCjksMDAwIGFzIGhpZ2hseSB2YXJpYWJsZSB1c2luZyBhIG1ldGhvZCBwdWJsaXNoZWQgaW4gQnJlbm5lY2tlIGV0LiBhbC4gMjAxMy4KV2UgcmV0YWluIHRob3NlIGdlbmVzIGZvciBmdXJ0aGVyIGFuYWx5c2lzLgoKV2UgYWxzbyBpbXBvcnQgYSB0YWJsZSB3aXRoIGdlbmVzIGFubm90YXRlZCBhcyB0aXNzdWUgcmVzdHJpY3RlZCBhbnRpZ2VucyAodHJhcykKCmZyb20gU2hpbnRvIGV0LiBhbC4gMjAxMyBhbmQgYW5ub3RhdGlvbiBmb3IgbW91c2UgZ2VuZXMgZnJvbSBFTlNFTUJMLgoKCmBgYHtyIGltcG9ydF9kYXRhfQpsb2FkKGZpbGUucGF0aChkYXRhX2RpciwgIm10ZWNfY291bnRzLlJEYXRhIikpCmxvYWQoZmlsZS5wYXRoKGRhdGFfZGlyLCAibXRlY19jZWxsX2Fubm8uUkRhdGEiKSkKbG9hZChmaWxlLnBhdGgoZGF0YV9kaXIsICJtdGVjX2dlbmVfYW5uby5SRGF0YSIpKQpsb2FkKGZpbGUucGF0aChkYXRhX2RpciwgInRyYXMuUkRhdGEiKSkKYGBgCgoKIyBHcmFwaGljcyBpbiBSCgojIGdncGxvdDJ7I2dnfQoKYHIgQ1JBTnBrZygiZ2dwbG90MiIpYCBpcyBhIHBhY2thZ2UgYnkgSGFkbGV5IFdpY2toYW0gIHRoYXQgaW1wbGVtZW50cyB0aGUgaWRlYSBvZiAKKmdyYW1tYXIgb2YgZ3JhcGhpY3MqIC0tIGEgY29uY2VwdCBjcmVhdGVkIGJ5IExlbGFuZCBXaWxraW5zb24gaW4gaGlzIGJvb2sgb2YgdGhlIHNhbWUgbmFtZS4gQ29tcHJlaGVuc2l2ZSBkb2N1bWVudGF0aW9uIGZvciB0aGUgcGFja2FnZSBjYW4KYmUgZm91bmQgb24gPGh0dHA6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvPi4gIFRoZSBvbmxpbmUgZG9jdW1lbnRhdGlvbiBpbmNsdWRlcwpleGFtcGxlIHVzZSBjYXNlcyBmb3IgZWFjaCBvZiB0aGUgZ3JhcGhpYyB0eXBlcyB0aGF0IGFyZSBpbnRyb2R1Y2VkIGluIHRoaXMgbGFiIChhbmQKbWFueSBtb3JlKSBhbmQgaXMgYW4gaW52YWx1YWJsZSByZXNvdXJjZSB3aGVuIGNyZWF0aW5nIGZpZ3VyZXMuCgpTaW5nbGUgY2VsbCBSTkEtU2VxIGRhdGEgaGFzIG1hbnkgemVybyBkdWUgdG8gYSBsaW1pdGVkIGNhcHR1cmUgZWZmaWNpZW5jeSAKYW5kIGJpYXNlcyBpbiB0aGUgcmV2ZXJzZSB0cmFuc2NyaXB0aW9uLiBJdCBpcyB0aHVzIGludGVyZXN0aW5nIHRvIHNlZSBob3cKdGhlIHRvdGFsIG51bWJlciBvZiBnZW5lcyBleHByZXNzZWQgKGF0IGxlYXN0IG9uZSBjb3VudCkgcmVsYXRlcyB0byAKdGhlIG51bWJlciBvZiBUUkEgZW5jb2RpbmcgZ2VuZXMgZXhwcmVzc2VkLiAKClRoZSBzaW5nbGUgY2VsbCBkYXRhIGFsc28gY29udGFpbnMgbVRFQyBjZWxscyBzZWxlY3QgYmFzZWQgb24gc3VyZmFjZSBtYXJrZXJzLAp3ZSBleGNsdWRlIHRob3NlIGFuZCBtYWtlIHN1cmUgdG8gaW5zcGVjdCBvbmx5IHByb3RlaW4gY29kaW5nIGdlbmVzLgpJbiBvcmRlciB0byBkbyB0aGlzLCB3ZSBmaXJzdCB0dXJuIHRoZSBjb3VudCB0YWJsZToKCmBgYHtyIG10ZWNfY291bnRfdGFibGV9Cm10ZWNfY291bnRzCmBgYAoKd2hpY2ggY29udGFpbnMgdGhlIGNvdW50cyBmb3IgZWFjaCBjZWxsIGluIHNlcGFydGUgY29sdW1ucyBpbnRvIGEgdGlkeSAKZGF0YSBmcmFtZS4gV2UgYGdhdGhlcmAgYWxsIGNvbHVtbnMgZXhjZXB0IGZvciB0aGUgZmlyc3Qgb25lIGFuZCB0aGVuCmFkZCBhIGNvbHVtbiB0ZWxsaW5nIHVzIHdoZXRoZXIgYSBzcGVjaWZpYyBnZW5lcyBpcyBjb2RpbmcgZm9yIGEgVFJBCmFuZCB3aGV0aGVyIGl0IHdhcyBkZXRlY3RlZCBvciBub3QuCgpUaGVuLCB3ZSBqb2luIHRoZSBhbm5vdGF0aW9uIHRvIHRoZSB0aWR5IHRhYmxlCgpgYGB7ciB0aWR5X2NvdW50fQptdGVjX2NvdW50c190aWR5IDwtIGdhdGhlcihtdGVjX2NvdW50cywga2V5ID0gImNlbGxfaWQiLCB2YWx1ZSA9ICJjb3VudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIC1lbnNlbWJsX2lkKSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgbXV0YXRlKGlzX3RyYSA9IGVuc2VtYmxfaWQgJWluJSB0cmFzJGdlbmUuaWRzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaXNfZGV0ZWN0ZWQgPSBjb3VudCA+IDApICU+JQogICAgICAgICAgICAgICAgICAgICAgICAgICBsZWZ0X2pvaW4obXRlY19jZWxsX2Fubm8sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9IGMoImNlbGxfaWQiID0gImNlbGxJRCIpKQpgYGAKCk5vdyB3ZSBjYW4gY29tcHV0ZSB0aGUgbnVtYmVyIG9mIFRSQSBnZW5lcyBkZXRlY3RlZCBwZXIgY2VsbCBhbmQgZmlsdGVyIAp0aGUgbm9uLS1wcm90ZWluIGNvZGluZyBnZW5lcyBhcyB3ZWxsIGFzIHRoZSBjZWxscyB0aGF0IGhhdmUgYmVlbiBwcm9jZXNzZWQKdXNpbmcgRkFDUyBiYXNlZCBvbiBhIHN1cmZhY2UgbWFya2VyLgoKRmluYWxseSwgd2Ugb2J0YWluIGEgdGlkeSB0YWJsZSB3aXRoIHR3byBjb2x1bW5zIHBlciBjZWxsOiB0aGUgbnVtYmVyIG9mIAp0cmFzIGFuZCBub24tdHJhcyBleHByZXNzZWQuIFRoZXJlIGlzIGEgc3VidGxlIGNoYW5nZSBpbiAgb2JzZXJ2YXRpb25zOiAKd2UgZ28gZnJvbSBnZW5lcyB3aXRoaW4gYSBzaW5nbGUgY2VsbCBhcyBvdXIgdW5pdCB0byBzaW5nbGUgY2VsbHMuCgpgYGB7ciB0cmFfcGVyX2NlbGx9Cgp0cmFfZGV0ZWN0ZWQgPC0gZmlsdGVyKG10ZWNfY291bnRzX3RpZHksIGlzX2RldGVjdGVkID09IFRSVUUsIAogICAgICAgICAgICAgICAgICAgICAgIFN1cmZhY2VNYXJrZXIgPT0gIk5vbmUiKSAlPiUKICAgICAgICAgICAgICAgIG11dGF0ZShpc190cmEgPSBpZmVsc2UoaXNfdHJhLCAidHJhIiwgIm5vdF9hX3RyYSIpKSAlPiUKICAgICAgICAgICAgICAgIGdyb3VwX2J5KGNlbGxfaWQsIGlzX3RyYSkgJT4lIAogICAgICAgICAgICAgICAgdGFsbHkoKSAlPiUKICAgICAgICAgICAgICAgIHNwcmVhZChrZXkgPSBpc190cmEsIHZhbHVlID0gbikgJT4lCiAgICAgICAgICAgICAgICBtdXRhdGUodG90YWxfZGV0ZWN0ZWQgPSBzdW0odHJhLCBub3RfYV90cmEpKQoKdHJhX2RldGVjdGVkCmBgYAoKCldlIHZpc3VhbGl6ZSB0aGlzIHJlbGF0aW9uc2hpcCBpbiBhIHNjYXR0ZXJwbG90IHdpdGggZ2dwbG90Mi4KCmBgYHtyIHRyYXZzYWxsLCBmaWcuY2FwPSJUb3RhbCBudW1iZXIgb2YgZ2VuZXMgdnMgVFJBIn0Kc2NhdHRlcl90cmEgPC0gZ2dwbG90KHRyYV9kZXRlY3RlZCwgYWVzKHggPSB0b3RhbF9kZXRlY3RlZCwgeSA9IHRyYSkpKyAKICAgICAgICAgICAgICAgZ2VvbV9wb2ludCgpICsKICAgICAgICAgICAgICAgY29vcmRfZXF1YWwoKQoKc2NhdHRlcl90cmEKYGBgCgoKV2UganVzdCB3cm90ZSBvdXIgZmlyc3QgInNlbnRlbmNlIiB1c2luZyB0aGUgZ3JhbW1hciBvZiBncmFwaGljcy4gCkxldCB1cyBkZWNvbnN0cnVjdCB0aGlzIHNlbnRlbmNlLgpGaXJzdCwgd2Ugc3BlY2lmaWVkIHRoZSBkYXRhZnJhbWUgdGhhdCBjb250YWlucyB0aGUgZGF0YSwgYG10ZWNfY291bnRzX3RpZHlgLgpUaGVuIHdlIHRvbGQgYGdncGxvdGAgdmlhIHRoZSBgYWVzYC4gVGhpcyBzdGFuZHMgZm9yIGBhZXN0aGV0aWNgIGFyZ3VtZW50IAooQWVzdGhldGljcyBhcmUgdmlzdWFsIHByb3BlcnR5IG9mIHRoZSBvYmplY3RzIGluIHlvdXIgcGxvdC4pCmFuZCB0ZWxscyBgciBDUkFOcGtnKCJnZ3Bsb3QyIilgIHdoaWNoIHZhcmlhYmxlcyAKd2Ugd2FudCBvbiB0aGUgXCh4XCktLSBhbmQgXCh5XCktLWF4ZXMsIHJlc3BlY3RpdmVseS4gCgpGaW5hbGx5LCB3ZSBzdGF0ZWQgdGhhdCB3ZSB3YW50IHRoZSBwbG90IHRvIHVzZSBwb2ludHMsIGJ5IGFkZGluZyB0aGUgcmVzdWx0IApvZiBjYWxsaW5nIHRoZSBmdW5jdGlvbiBgZ2VvbV9wb2ludGAuIFRoZSBwbG90IHdvdWxkIGJlIGdvb2QgdG8gZ28gbm93LCBidXQKYXMgYm90aCBheGVzIHJlcHJlc2VudCBjb3VudHMgYW5kIHRodXMgaGF2ZSB0aGUgc2FtZSB1bml0cywgCndlIHdhbnQgdGhlaXIgYXNwZWN0IHJhdGlvIHRvIGJlIGVxdWFsLiAKCk5vdGUgdGhhdCBpbiBgciBDUkFOcGtnKCJnZ3Bsb3QyIilgLCB3ZSBidWlsZCBhIHBsb3QgImxheWVyIGJ5IGxheWVyIjoKV2UgZmlyc3Qgc3BlY2lmeSBhIG1hcHBpbmcgZnJvbSB0aGUgZGF0YSB0byBhZXN0aGV0aWNzIGFuZCB0aGVuIHVzZSBhbgphcHByb3ByaWF0ZSBnZW9tZXRyeSB0byBkaXNwbGF5IGl0LiBXZSBjYW4gYWx3YXlzIGFkZCBuZXcgbGF5ZXJzIG9yIG1vZGlmeQpleGlzdGluZyBvbmVzIGJ5IGxpdGVyYWxseSBhZGRpbmcgdGhlbSwgaGVyZSB3ZSBhZGQgaG9yaXpvbnRhbCBsaW5lcwppbmRpY2F0aW5nIG91ciBtYXJnaW5hbCBjb3VudHMgdG8gdGhlIHNjYXR0ZXIgcGxvdC4gVGhpcyBpcyBrbm93biAKYXMgYSAicnVnIHBsb3QiCgpgYGB7ciBtb3JlX2xheWVyc30Kc2NhdHRlcl90cmEgKyAKICBnZW9tX3J1ZyhhbHBoYSA9IEkoMC4yKSkKYGBgCgpXZSBzZXQgKGluc3RlYWQgb2YgbWFwcGVkKSB0aGUgYWxwaGEgdmFsdWUgdG8gMC4yLCB0aGlzIGluY3JlYXNlc2VzIHRoZSAKdHJhbnNwYXJhbmN5IG9mIHRoZSBydWdzIGFuZCBhbGxldmlhdGVzIG92ZXJwbG90dGluZy4KCgojIyBFeGVyY2lzZTogR2VvbWV0cmllcyBhbmQgYXNwZWN0IHJhdGlvcwoKMS4gV2hhdCBoYXBwZW5zIGlmIHlvdSBvbWl0IHRoZSBjYWxscyB0byBgZ2VvbV9wb2ludCgpYCBhbmQKICAgYGNvb3JkX2VxdWFsKClgID8KICAgCjIuIFVzZSB0aGUgaW50ZXJuZXQgKGUuZy4gPGh0dHA6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvPikgdG8gZmluZCAKb3V0IGhvdyB0byBhZGQgYSBzbW9vdGhpbmcgY3VydmUgdG8gdGhlIHBsb3QuIENhbiB5b3UgY2hhbmdlIHRoZSBzbW9vdGhlcgp0byBhIHJlZ3Jlc3Npb24gbGluZT8KCgpgYGB7ciBleF9nZW9tfQpnZ3Bsb3QodHJhX2RldGVjdGVkLCBhZXMoeCA9IHRvdGFsX2RldGVjdGVkLCB5ID0gdHJhKSkKCnNjYXR0ZXJfdHJhICsKICBnZW9tX3Ntb290aChjb2xvciA9ICJjb3JhbDMiKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIikKYGBgCgpBcyBGaWd1cmUgXEByZWYoZmlnOnRyYXZzYWxsKSBzaG93cyB0aGUgdHdvIG51bWJlcnMgc2VlbSB2ZXJ5IGNvcnJlbGF0ZWQgCmFuZCBzZWVtIHRvIGZvbGxvdyBhIGxpbmVhciByZWxhdGlvbiwgCmluZGljYXRpbmcgdGhhdCB0aGUgbnVtYmVyIG9mIFRSQSBnZW5lcyB0aGF0IGFyZSBkZXRlY3RlZCB3aXRoaW4gYSBjZWxsIAppcyBwcm9wb3J0aW9uYWwgdG8gdGhlIG51bWJlciBvZiBkZXRlY3RlZCBnZW5lcy4gUmVncmVzc2lvbiBtb2RlbHMgCmFsbG93IGZvciB0aGUgc3lzdGVtYXRpYyBjaGFyYWN0ZXJpemF0aW9uIG9mIHN1Y2ggcmVsYXRpb25zaGlwcy4gCgojIFJlZ3Jlc3Npb24gbW9kZWxzCgpJbiByZWdyZXNzaW9uIHdlIHVzZSBvbmUgdmFyaWFibGUgdG8gZXhwbGFpbiBvciBwcmVkaWN0IHRoZSBvdGhlci4gSXQgaXMgY3VzdG9tYXJ5CnRvIHBsb3QgdGhlIHByZWRpY3RvciB2YXJpYWJsZSBvbiB0aGUgeC0tYXhpcyBhbmQgdGhlIHByZWRpY3RlZCB2YXJpYWJsZSBvbiB0aGUKeS0tYXhpcy4KVGhlIHByZWRpY3RvciBpcyBhbHNvIGNhbGxlZCB0aGUgaW5kZXBlbmRlbnQgdmFyaWFibGUsIHRoZSBleHBsYW5hdG9yeSB2YXJpYWJsZSwKdGhlIGNvdmFyaWF0ZSwgb3Igc2ltcGx5IHguIFRoZSBwcmVkaWN0ZWQgdmFyaWFibGUgaXMgY2FsbGVkIHRoZSBkZXBlbmRlbnQgCnZhcmlhYmxlLCBvciBzaW1wbHkgeS4KCkluIGEgcmVncmVzc2lvbiBwcm9ibGVtIHRoZSBkYXRhIGFyZSBwYWlycyBcKCh4X2kgLCB5X2kgKVwpIGZvciBcKGkgPSAxLCBcZG90c2MgLCBuXCkuCkZvciBlYWNoICBcKHhfaSBcKSwgXCh5X2lcKSBpcyBhIHJhbmRvbSB2YXJpYWJsZSB3aG9zZSBkaXN0cmlidXRpb24gZGVwZW5kcyBvbiAgXCh4X2kgXCkuIApUaGUgcmVncmVzc2lvbiBtb2RlbCBpczoKClxbCnlfaSA9IGcoeF9pKSArIFx2YXJlcHNpbG9uX2kgLgpcXQoKVGhlIGFib3ZlICBleHByZXNzZXMgXCh5X2lcKSBhcyBhIHN5c3RlbWF0aWMgcGFydCBcKGcoeF9pKVwpCmFuZCBhbiB1bmV4cGxhaW5lZCBwYXJ0CiRcdmFyZXBzaWxvbl9pJC4gT3IgbW9yZSBpbmZvcm1hbGx5OiAKCl9fcmVzcG9uc2UgPSBzaWduYWwgKyBub2lzZV9fCgpcKGdcKSBpcyBjYWxsZWQgdGhlIHJlZ3Jlc3Npb24gZnVuY3Rpb24sIGFuZCByZWdyZXNzaW9uIG1vZGVscyBwcm92aWRlIG1lYW5zCnRvIGNvbXB1dGUgXChnXCkuIEEgY29tbW9uIGNob2ljZSBmb3IgXChnXCkgaXMgYSBsaW5lYXIgZnVuY3Rpb24sIGlmIHdlIGhhdmUgb25seQpzaW5nbGUgcHJlZGljdG9yICRYJCwgdGhlIHNpbXBsZSBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbCBpczoKClxbCnlfaSA9IGEgKyAgYiB4X3tpfSArIFx2YXJlcHNpbG9uX2k7IFxxdWFkCiBcdmFyZXBzaWxvbl9pIFxzaW0gTigwLCBcc2lnbWEpOwpcXQoKV2UgY2FuIG9mIGNvdXJzZSBhbHdheXMgYWRkIG1vcmUgcHJlZGljdG9ycyB0byB0aGUgbGluZWFyIGZ1bmN0aW9uLiBUaGUgY29lZmZpY2llbnQKXChiXCkgaXMgY2FsbGVkIHRoZSBfX3Nsb3BlX18gYW5kIFwoYVwpIGlzIGNhbGxlZCB0aGUgX19pbnRlcmNlcHRfXyAuCgpXZSBjYW4gZml0IGEgbGluZWFyIHJlZ3Jlc3Npb24gdmlhIGEgY2FsbCB0byB0aGUgZnVuY3Rpb24gYGxtKClgLiBUaGUgcmVncmVzc2lvbgptb2RlbCBpcyBzcGVjaWZpZWQgdXNpbmcgUidzIGZvcm11bGEgbm90YXRpb24uIAoKYGBge3IgcmVncmVzc3Npb25fdHJhfQpsbV90cmEgPC0gbG0odHJhIH4gdG90YWxfZGV0ZWN0ZWQsIGRhdGEgPSB0cmFfZGV0ZWN0ZWQpCmxtX3RyYQpgYGAKCkFzIHdlIGNhbiBzZWUsIHRoZSBlc3RpbWF0ZWQgCnNsb3BlIGlzIH4gYHIgdGlkeShsbV90cmEsIHF1aWNrID0gVFJVRSlbMiwgImVzdGltYXRlIl1gLCBpbmRpY2F0aW5nIAp0aGF0IHdlIGhhdmUgYSBwcm9wb3J0aW9uIG9mIGByIHRpZHkobG1fdHJhLCBxdWljayA9IFRSVUUpWzIsICJlc3RpbWF0ZSJdYCAKdHJhIGV4cHJlc3NpbmcgZ2VuZXMgb24gYXZlcmFnZSBwZXIgY2VsbCBmb3IgdGhlIGhpZ2hseSB2YXJpYWJsZSBnZW5lcy4gClRoaXMgaXMgaW4gbGluZSB3aXRoIHRoZSBzdXBwbGVtZW50YXJ5IGZpZ3VyZSAxIG9mIHRoZSBvcmlnaW5hbCBwdWJsaWNhdGlvbiwKd2hpY2ggdXNlcyB0aGUgZnVsbCBzZXQgb2YgZXhwcmVzc2VkIGdlbmVzLCBub3QganVzdCB0aGUgaGlnaGx5IHZhcmlhYmxlIApvbmVzLgoKIyBMb2NhbCByZWdyZXNzaW9uICAoTE9FU1MpCgoKCkxvY2FsIHJlZ3Jlc3Npb24gaXMgYSBjb21tb25seSB1c2VkIGFwcHJvYWNoIGZvciBmaXR0aW5nIGZsZXhpYmxlIG5vbi0tbGluZWFyCmZ1bmN0aW9ucywgd2hpY2ggaW52b2x2ZXMgY29tcHV0aW5nIG1hbnkgbG9jYWwgbGluZWFyIHJlZ3Jlc3Npb24gZml0cyBhbmQgY29tYmluaW5nCnRoZW0uIExvY2FsIHJlZ3Jlc3Npb24gaXMgYSB2ZXJ5IHVzZWZ1bCB0ZWNobmlxdWUgYm90aCBmb3IgIGRhdGEgdmlzdWFsaXphdGlvbiBhbmQKdHJlbmQgZml0dGluZy4gRml0dGluZyBtYW55IGxvY2FsIG1vZGVscyByZXF1aXJlcyBxdWl0ZSBzb21lIGNvbXB1dGF0aW9uYWwgcG93ZXIsCmJ1dCBpdCB1c3VhbGx5IGZlYXNpYmxlIHdpdGggdG9kYXkncyBoYXJkd2FyZS4gV2UgaWxsdXN0cmF0ZSB0aGUgbG9jYWwgcmVncmVzc2lvbgp1c2luZyB0aGUgYHIgQ1JBTnBrZygibG9jZml0IikgYCBwYWNrYWdlIG9uIHNpbXVsYXRlZCBkYXRhLgoKV2UgZmlyc3QgZml0IGEgbGluZWFyIHJlZ3Jlc3Npb24gbGluZSB0byBzaW11bGF0ZWQKZGF0YSB0aGF0IGZvbGxvd3MgYSBwb2x5bm9taWFsIHRyZW5kIGFuZCBzZWUgdGhhdCBpdCBkb2VzIG5vdCByZWFsbHkKZml0IHdlbGwuCgpgYGB7ciBsb2Vzc0V4YW1wbGVMaW5GaXQsIGRlcGVuZHNvbj0iZml0X21vZGVsIn0KCiMgY3JlYXRlX2RhdGEKeSA8LSBzZXEoZnJvbT0xLCB0bz0xMCwgbGVuZ3RoLm91dD0xMDApCmEgPC0geV4zICt5XjIgICsgcm5vcm0oMTAwLG1lYW49MCwgc2Q9MzApCmRhdGFMIDwtIGRhdGEuZnJhbWUoYT1hLCB5PXkpCnFwbG90KHksIGEsIGRhdGEgPSBkYXRhTCkKCiMgbGluZWFyIGZpdApsaW5yZWcgPC0gbG0oYX55LCBkYXRhID0gZGF0YUwpCgoKKHFwbG90KHksIGEsIGRhdGEgPSBkYXRhTCkgKwogIGdlb21fYWJsaW5lKHNsb3BlID0gdGlkeShsaW5yZWcsIHF1aWNrID0gVFJVRSlbMiwyXSwgCiAgICAgICAgICAgICAgaW50ZXJjZXB0ID0gdGlkeShsaW5yZWcsIHF1aWNrID0gVFJVRSlbMSwyXSkpCnRpZHkobGlucmVnKQoKZGF0YUwkTGluUmVnIDwtIHByZWRpY3QobGlucmVnKQpgYGAKCldlIG5vdyB1c2UgdGhlIGZ1bmN0aW9uIGBsb2NmaXRgIHRvIHBlcmZvcm0gYSBsb2NhbCByZWdyZXNzaW9uIG9uIHRoZQpkYXRhLiBJdCB0YWtlcyB0aGUgcHJlZGljdG9ycyB3cmFwcGVkIGluIGEgY2FsbCB0byBgbHAoKWAuIFdpdGhpbiB0aGlzCmZ1bmN0aW9uIHdlIGNhbiBhbHNvIHNldCB0dW5uaW5nIHBhcmFtZXRlcnMuIEFuIGltcG9ydGFudCBvbmUgaXMgdGhlICBgbm5gCm9uZSwgd2hpY2ggc2V0IHRoZSBwcm9wb3J0aW9uIG9mIG5lYXJlc3QtLW5laWdoYm9ycyB0byBiZSB1c2VkIGZvciB0aGUgbG9jYWwgZml0cy4KClRoZSBsb3dlciB0aGlzIHBlcmNlbnRhZ2UsIHRoZSBtb3JlIGNsb3NlbHkgdGhlIGxpbmUgd2lsbCBmb2xsb3cgdGhlIGRhdGEgcG9pbnRzLgoKYGBge3IgbG9lc3NFeGFtcGxlRml0LCBkZXBlbmRzb249ImxvZXNzRXhhbXBsZUxpbkZpdCJ9CgpkYXRhTCRsb2NGaXQgIDwtIHByZWRpY3QobG9jZml0KHl+bHAoYSwgbm49MC41LCBkZWc9MSksIGRhdGE9ZGF0YUwpLAogICAgICAgICAgICAgICAgICAgICAgICAgbmV3ZGF0YSA9IGRhdGFMJGEpCgoKIChxcGxvdChhLCB5LCBkYXRhID0gZGF0YUwsIG1haW4gPSAiTGluZWFyIHZzLiBsb2NhbCByZWdyZXNzaW9uIikKICsgIGdlb21fbGluZShhZXMoeCA9IGEsIHkgPSBsb2NGaXQpLCBjb2xvciA9ICJkb2RnZXJibHVlMyIpCiArICBnZW9tX2xpbmUoYWVzKHggPSBhLCB5ID0gTGluUmVnKSwgY29sb3IgPSAiY29yYWwzIikpCgpgYGAKCgoKIyBOb3JtYWxpemF0aW9uIGFuZCBjb25mb3VuZGluZyBmYWN0b3JzLgoKCiMjIE5vcm1hbGl6YXRpb24gb2Ygc2luZ2xlIGNlbGwgZGF0YQoKKiB1c2Ugc2NyYW4gc2l6ZSBmYWN0b3JzCgojIyBDb25mb3VuZGluZyBmYWN0b3JzCgoqIFpJTkJBIFdhdmUKCgoKIyBTZXNzaW9uIEluZm8KCmBgYHtyIHNlc3Npb25faW5mbywgY2FjaGUgPSBGQUxTRX0Kc2Vzc2lvbkluZm8oKQpgYGAKCgoKCg==