PREAMBLE
Purpose of this
session
This file describes the different steps to perform the third
part of the single cell RNAseq data analysis training course
for the EBAII n1 2024, covering these steps :
Filtering of bad cells and extreme
low-expression features
Estimation of the cell cycle phase
Identification and removal of cell
doublets
Warm-up
- We set common parameters we will use throughout
this session :
## Set your project name
project_name <- "ebaii_sc_teachers" # Do not copy-paste this ! It's MY project ! Put yours !!
## Seed for the RNG
my_seed <- 1337L
## Minimum cells with counts to keep a feature
min_cells <- 5
Prepare the data
structure
We will do the same as for former steps, just changing the session
name :
Main directory
## Preparing the path
TD_dir <- paste0("/shared/projects/", project_name, "/SC_TD")
## Creating the root directory
dir.create(path = TD_dir, recursive = TRUE)
## Print the root directory on-screen
print(TD_dir)
[1] "/shared/projects/ebaii_sc_teachers/SC_TD"
Current session
## Creating the session (Preproc.3) directory
session_dir <- paste0(TD_dir, "/03_Preproc.3")
dir.create(path = session_dir, recursive = TRUE)
## Print the session directory on-screen
print(session_dir)
[1] "/shared/projects/ebaii_sc_teachers/SC_TD/03_Preproc.3"
Genelists
directory
res_dir <- paste0(TD_dir, "/Resources")
glist_dir <- paste0(res_dir, "/Genelists")
## Print the resources directory on-screen
print(glist_dir)
[1] "/shared/projects/ebaii_sc_teachers/SC_TD/Resources/Genelists"
Output directory
## Creating the OUTPUT data directory
output_dir <- paste0(session_dir, "/RESULTS")
dir.create(path = output_dir, recursive = TRUE)
## Print the output directory on-screen
print(output_dir)
[1] "/shared/projects/ebaii_sc_teachers/SC_TD/03_Preproc.3/RESULTS"
Reload the Seurat
Object
- We can reload the object we saved at the former step
## The latest Seurat object saved as RDS (name)
sobj_file <- "02_TD3A_S5_Metrics.Bio_31053.4587.RDS"
## The latest Seurat object saved as RDS (full path)
sobj_path <- paste0(TD_dir,
"/02_Preproc.2/RESULTS/",
sobj_file)
force <- FALSE ## To force a re-download of a Zenodo-hosted backup
local <- FALSE ## To force a loading from a local backup
## In case of error/lost data : force a reload from a Zenodo backup repository
if(force) {
zen_id <- "14035293"
zen_backup_file <- paste0("https://zenodo.org/records/",
zen_id,
"/files/",
sobj_file)
download.file(url = zen_backup_file,
destfile = sobj_path)
}
## In case of error/lost data : force a reload from a local backup repository
if(local) {
sobj_path <- paste0(
"/shared/projects/2422_ebaii_n1/atelier_scrnaseq/TD/BACKUP/RDS/",
sobj_file)
}
## Load the object
sobj <- readRDS(file = sobj_path)
Now, we finally have all the metrics and bias sources we could use,
so we can actually get to the filtering step.
Filtering
Features
As already explained, the sparsity of the count
matrix is high, very high.
To the point that there probably are some features that have
so low expression level that they were only counted in
very few cells. As such, they have absolutely
no chance to characterize any cell type, nor harbor
some variation between different cell types.
As such, we should discard these
features
What are the actual dimensions of our expression data ?
## Dimensions of our Seurat object
dim(sobj)
Show output
[1] 31053 4587
We want to remove the features that are expressed in less than
5 cells.
First, we quantify, for each feature, the number of cells with 0
count.
## Compute the amount of 0s per feature
nFeat_zero <- sparseMatrixStats::rowCounts(
x = SeuratObject::GetAssayData(
object = sobj,
assay = "RNA",
layer = "counts"),
value = 0)
## Inversion : computing the number of cells with at least one count, per feature
nFeat_nonzero <- ncol(sobj) - nFeat_zero
## Identify those with at least 5 cells with expression
nFeat_keep <- nFeat_nonzero >= min_cells
## Quantify the selected ones
table(nFeat_keep)
Show output
nFeat_keep
FALSE TRUE
18545 12508
And now we can discard features expressed in less
than 5 cells :
filtc5_pre <- SC.helper::QnD_viz(sobj = sobj,
return_plot = TRUE)
Show plot

## Restrict the Seurat object
sobj <- sobj[nFeat_keep,]
What are the Seurat object dimensions, now ?
Show output
[1] 12508 4587
We can visualize the cell space after this features
filtering
filtc5_post <- SC.helper::QnD_viz(sobj = sobj,
return_plot = TRUE)
Show plot

Merging plots for ease of visualization :
## Using the patchwork package to merge plots (and ggplot2 to add titles)
patchwork::wrap_plots(
list(
filtc5_pre & ggplot2::ggtitle(label = "BEFORE"),
filtc5_post & ggplot2::ggtitle(label = "AFTER")),
nrow = 1)
Show plot

Question :
Do you see any difference when comparing before vs after features filtering ?
## . Not much changed, maybe cell groups seem a bit more condensed
##
## . This is expected, as we removed features with almost no expression
Cells
We are now able to apply all the filtering
strategies we established for each QC metric.
## Identify the cells to keep
bc_keep <- sobj$nFeature_RNA_in_range &
sobj$nCount_RNA_in_range &
sobj$percent_mt_in_range &
sobj$percent_rb_in_range &
sobj$percent_st_in_range
## Contengency
table(bc_keep)
Show output
bc_keep
FALSE TRUE
309 4278
Apply the filter
## Seurat object BEFORE cell filtering
dim(sobj)
Show output
[1] 12508 4587
## Apply cell filtering
sobj <- subset(x = sobj, cells = colnames(sobj)[bc_keep])
## Seurat object AFTER cell filtering
dim(sobj)
Show output
[1] 12508 4278
We can visualize the cell space since this cell
filtering
SC.helper::QnD_viz(sobj = sobj)
Show plot

Question
Do you see any difference when comparing before vs after features filtering ?
## . Not much changed as well (we did not discard many cells)
##
## . The biggest cluster structure seems more defined
Save the Seurat
object
We will save our Seurat object that now contains
filtered cells and features :
## Save our Seurat object (rich naming)
out_name <- paste0(
output_dir, "/", paste(
c("03", Seurat::Project(sobj), "S5",
"Metrics.Filtered", paste(
dim(sobj),
collapse = '.'
)
), collapse = "_"),
".RDS")
## Check
print(out_name)
[1] "/shared/projects/ebaii_sc_teachers/SC_TD/03_Preproc.3/RESULTS/03_TD3A_S5_Metrics.Filtered_12508.4278.RDS"
## Write on disk
saveRDS(object = sobj,
file = out_name)
Cell cycle scores
We are currently analyzing independent profiles
from isolated cells, from a sample
dissociation
As such, cells were most probably not
synchronized, thus the effect of their cell cycle
state on genes expression may be strong, to the point that it
can bias the data (ie, mask some lower amplitude
biological variation).
In order to assess (and maybe, remove)
this bias, we have to quantify it.
We will perform this estimation thanks to heuristics based on
knowledge : Seurat includes a method that evaluates the
cell cycle phase of cells through scores for the S and G2M
phases, each based on phase-specific gene
signatures.
For this step, we will use additional gene lists from knowledge
(cell cycle phase), hosted in a Zenodo respository (Id : 14037355)
Load gene lists
## The cell cycle gene lists file
cc_file <- paste0(glist_dir,
"/",
cc_file)
## Load the cell cycle reference genes lists
cc_genes <- readRDS(file = cc_file)
## Have a look on its content
str(cc_genes)
Show output
List of 2
$ s.genes : chr [1:43] "Mcl5" "Pcna" "Tyms" "Fen1" ...
$ g2m.genes: chr [1:54] "Hmgb2" "Cdk1" "Nusap1" "Ube2c" ...
Question
As explained, we will use gene lists extracted from community knowledge.
Our data contain values for genes also, so we will cross them.
Is there something we should check ?
## . We may check if the genes in our gene lists are effectively
## present in our Seurat object !
##
## . This is expected, as we removed features with almost no expression
Beyond :
Write a code that performs this check
Add a code to adjust the content of the gene lists accordingly
(remove genes from the gene lists that are not present in our
dataset)
(NOTE : this is actually not needed as already checked and
corrected by the cell cyle estimation method we will use)
## Check if our data genes cover the gene lists
lapply(cc_genes, function(x) x %in% rownames(sobj))
## Remove genes not in sobj
cc_genes <- lapply(cc_genes, function(gl) { gl[gl %in% rownames(sobj)] })
## Check the modification
str(cc_genes)
## Check that all genes are available in sobj
all(unique(unlist(cc_genes)) %in% rownames(sobj))
Visualization
As usual, we can visualize the results as violins :
Seurat::VlnPlot(object = sobj,
features = c("CC_Seurat_S.Score",
"CC_Seurat_G2M.Score",
"CC_Seurat_SmG2M.Score"))
Show plot

But it’s not that easy to interpret…
Let’s plot it in the cell space
“S minus G2M”
(SmG2M
) score :
## Using the 'features' parameter to plot the SmG2M score (continuous data)
## Here, we will keep the modified Seurat object to speed up further plots
VIZ <- SC.helper::QnD_viz(sobj = sobj,
features = "CC_Seurat_SmG2M.Score",
return_object = TRUE)
Show plot

Estimated cell
phase :
## Using the 'group_by' parameter to plot the estimated phases (categorical data)
## Here, we recycle keep the VIZ Seurat object that contains everything to perform the plot without computing it again
SC.helper::QnD_viz(sobj = VIZ,
slot = NULL,
dimred = "umap",
group_by = "CC_Seurat_Phase")
Show plot

## VIZ is not needed anymore
rm(VIZ)
Questions
Does the structure of the cells in this representation seem to have a link with these cell cycle phases/scores ?
## It's an absolute yes !
Do you think this is the result of an artifact, or
biology-related ?
Save the Seurat
object
We will save our Seurat object that now contains the cell cycle
states/scores :
## Save our Seurat object (rich naming)
out_name <- paste0(
output_dir, "/", paste(
c("04", Seurat::Project(sobj), "S5",
"CC", paste(
dim(sobj),
collapse = '.'
)
), collapse = "_"),
".RDS")
## Check
print(out_name)
[1] "/shared/projects/ebaii_sc_teachers/SC_TD/03_Preproc.3/RESULTS/04_TD3A_S5_CC_12508.4278.RDS"
## Write on disk
saveRDS(object = sobj,
file = out_name)
Cell doublets
We will use two different methods to detect and
remove cell doublets :
None of the methods accepts a SeuratObject
as input, but
a SingleCellExperiment
object.
Hopefully :
- Seurat has a function to perform the conversion
- The output results can be integrated into our Seurat object with
ease
scds
## Fix seed
set.seed(my_seed)
## Run scds
sobj$doublet_scds.hybrid <- unname(
scds::cxds_bcds_hybrid(
Seurat::as.SingleCellExperiment(
sobj, assay = "RNA"))$hybrid_score > 1)
## Contingencies
table(sobj$doublet_scds.hybrid)
Show output
FALSE TRUE
4072 206
scDblFinder
## Fix seed
set.seed(my_seed)
## Run scDblFinder (which needs another object type)
sobj$doublet_scDblFinder <- scDblFinder::scDblFinder(
sce = Seurat::as.SingleCellExperiment(
x = sobj,
assay = "RNA"),
returnType = "table")$class == "doublet"
## Contingencies
table(sobj$doublet_scDblFinder)
Show output
FALSE TRUE
4100 178
Merge results
We merge results of the two methods
## Logical union of both methods
sobj$doublet_union <- sobj$doublet_scds.hybrid | sobj$doublet_scDblFinder
## Quantify doublets
table(sobj$doublet_union)
Show output
FALSE TRUE
4035 243
We can assess tool-specific and
common doublets
### Singlets by default
sobj$doublet_viz <- "singlet"
### Union
sobj$doublet_viz[sobj$doublet_union] <- "both"
### scds-specific
sobj$doublet_viz[sobj$doublet_scds.hybrid & !sobj$doublet_scDblFinder] <- "scds"
### scDblFinder-specific
sobj$doublet_viz[sobj$doublet_scDblFinder & !sobj$doublet_scds.hybrid] <- "scDblFinder"
## Convert to factor
sobj$doublet_viz <- as.factor(sobj$doublet_viz)
## Contingencies
table(sobj$doublet_viz)
Show output
both scDblFinder scds singlet
141 37 65 4035
Beyond : Create an upset-plot for the doublet status
according to the two methods used
## Build a list of cells tagged by one tool, the other, or both
dbl_list <-list(
"scds" = colnames(sobj)[sobj$doublet_scds.hybrid & !sobj$doublet_scDblFinder],
"scDblFinder" = colnames(sobj)[!sobj$doublet_scds.hybrid & sobj$doublet_scDblFinder],
"both" = colnames(sobj)[sobj$doublet_scds.hybrid & sobj$doublet_scDblFinder])
## Draw the upset plot
UpSetR::upset(data = UpSetR::fromList(dbl_list),
nintersects = NA,
sets = rev(names(dbl_list)),
keep.order = TRUE,
order.by = "freq")
Show plot

Now we can remove barcodes identified as cell doublets, and visualize
the cell space before and after.
Doublets filtering
BEFORE
### Doublets viz (before removal)
umap_dbl_unfilt <- SC.helper::QnD_viz(
sobj = sobj,
group_by = "doublet_viz",
return_plot = TRUE)
Show plot

Remove
doublets
## Dimensions before removal
dim(sobj)
[1] 12508 4278
## Perform the removal
sobj <- sobj[,!sobj$doublet_union]
## Dimensions after
dim(sobj)
[1] 12508 4035
AFTER
### Doublets viz (after removal)
umap_dbl_filt <- SC.helper::QnD_viz(
sobj = sobj,
group_by = "doublet_viz",
return_plot = TRUE)
Show plot

Merge plots for ease of use :
## Using the patchwork package to merge plots (and ggplot2 to add titles)
patchwork::wrap_plots(
list(
umap_dbl_unfilt & ggplot2::ggtitle(label = "Cell doublets (unfiltered)"),
umap_dbl_filt & ggplot2::ggtitle(label = "Cell doublets (filtered)")),
nrow = 1)
Show plot

Question
What do you observe when comparing before and after the doublets filtering ?
Save the Seurat
object
We will save our Seurat object that is now filtered for doublets
:
## Save our Seurat object (rich naming)
out_name <- paste0(
output_dir, "/", paste(
c("05", Seurat::Project(sobj), "S5",
"Doublets.Filtered", paste(
dim(sobj),
collapse = '.'
)
), collapse = "_"),
".RDS")
## Check
print(out_name)
Show output
[1] "/shared/projects/ebaii_sc_teachers/SC_TD/03_Preproc.3/RESULTS/05_TD3A_S5_Doublets.Filtered_12508.4035.RDS"
## Write on disk
saveRDS(object = sobj,
file = out_name)
Rsession
Show output
R version 4.4.1 (2024-06-14)
Platform: x86_64-conda-linux-gnu
Running under: Ubuntu 20.04.6 LTS
Matrix products: default
BLAS/LAPACK: /shared/ifbstor1/software/miniconda/envs/r-4.4.1/lib/libopenblasp-r0.3.27.so; LAPACK version 3.12.0
locale:
[1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C
[3] LC_TIME=en_US.UTF-8 LC_COLLATE=en_US.UTF-8
[5] LC_MONETARY=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8
[7] LC_PAPER=en_US.UTF-8 LC_NAME=C
[9] LC_ADDRESS=C LC_TELEPHONE=C
[11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C
time zone: Europe/Paris
tzcode source: system (glibc)
attached base packages:
[1] stats graphics grDevices utils datasets methods base
other attached packages:
[1] SeuratObject_5.0.2 sp_2.1-4
loaded via a namespace (and not attached):
[1] bitops_1.0-9 matrixStats_1.4.1
[3] spatstat.sparse_3.1-0 SC.helper_0.0.6
[5] httr_1.4.7 RColorBrewer_1.1-3
[7] doParallel_1.0.17 alabaster.base_1.4.1
[9] tools_4.4.1 sctransform_0.4.1
[11] backports_1.5.0 utf8_1.2.4
[13] R6_2.5.1 HDF5Array_1.32.0
[15] lazyeval_0.2.2 uwot_0.2.2
[17] rhdf5filters_1.16.0 GetoptLong_1.0.5
[19] withr_3.0.1 gridExtra_2.3
[21] progressr_0.14.0 cli_3.6.3
[23] Biobase_2.64.0 spatstat.explore_3.3-2
[25] fastDummies_1.7.4 labeling_0.4.3
[27] alabaster.se_1.4.1 sass_0.4.9
[29] Seurat_5.1.0 spatstat.data_3.1-2
[31] ggridges_0.5.6 pbapply_1.7-2
[33] Rsamtools_2.20.0 foreign_0.8-86
[35] scater_1.32.1 parallelly_1.38.0
[37] limma_3.60.6 rstudioapi_0.17.0
[39] RSQLite_2.3.7 BiocIO_1.14.0
[41] generics_0.1.3 shape_1.4.6.1
[43] ica_1.0-3 spatstat.random_3.3-2
[45] dplyr_1.1.4 Matrix_1.7-1
[47] ggbeeswarm_0.7.2 fansi_1.0.6
[49] S4Vectors_0.42.1 abind_1.4-8
[51] lifecycle_1.0.4 SoupX_1.6.2
[53] yaml_2.3.10 edgeR_4.2.2
[55] SummarizedExperiment_1.34.0 rhdf5_2.48.0
[57] SparseArray_1.4.8 BiocFileCache_2.12.0
[59] Rtsne_0.17 grid_4.4.1
[61] blob_1.2.4 promises_1.3.0
[63] dqrng_0.4.1 ExperimentHub_2.12.0
[65] crayon_1.5.3 miniUI_0.1.1.1
[67] lattice_0.22-6 beachmat_2.20.0
[69] cowplot_1.1.3 KEGGREST_1.44.0
[71] pillar_1.9.0 knitr_1.48
[73] ComplexHeatmap_2.20.0 metapod_1.12.0
[75] GenomicRanges_1.56.2 rjson_0.2.21
[77] xgboost_1.7.8.1 future.apply_1.11.2
[79] codetools_0.2-20 leiden_0.4.3.1
[81] glue_1.8.0 spatstat.univar_3.0-1
[83] data.table_1.16.2 gypsum_1.0.1
[85] vctrs_0.6.5 png_0.1-8
[87] spam_2.11-0 gtable_0.3.5
[89] cachem_1.1.0 xfun_0.48
[91] S4Arrays_1.4.1 mime_0.12
[93] survival_3.7-0 SingleCellExperiment_1.26.0
[95] iterators_1.0.14 statmod_1.5.0
[97] bluster_1.14.0 fitdistrplus_1.2-1
[99] ROCR_1.0-11 nlme_3.1-165
[101] bit64_4.5.2 scds_1.20.0
[103] alabaster.ranges_1.4.1 filelock_1.0.3
[105] RcppAnnoy_0.0.22 UpSetR_1.4.0
[107] GenomeInfoDb_1.40.1 bslib_0.8.0
[109] irlba_2.3.5.1 vipor_0.4.7
[111] KernSmooth_2.23-24 rpart_4.1.23
[113] colorspace_2.1-1 BiocGenerics_0.50.0
[115] DBI_1.2.3 Hmisc_5.1-3
[117] celldex_1.14.0 nnet_7.3-19
[119] ggrastr_1.0.2 tidyselect_1.2.1
[121] bit_4.5.0 compiler_4.4.1
[123] curl_5.2.3 httr2_1.0.1
[125] htmlTable_2.4.2 BiocNeighbors_1.22.0
[127] DelayedArray_0.30.1 plotly_4.10.4
[129] rtracklayer_1.64.0 checkmate_2.3.1
[131] scales_1.3.0 lmtest_0.9-40
[133] rappdirs_0.3.3 stringr_1.5.1
[135] digest_0.6.37 goftest_1.2-3
[137] spatstat.utils_3.1-0 alabaster.matrix_1.4.1
[139] rmarkdown_2.28 XVector_0.44.0
[141] htmltools_0.5.8.1 pkgconfig_2.0.3
[143] base64enc_0.1-3 SingleR_2.6.0
[145] sparseMatrixStats_1.16.0 MatrixGenerics_1.16.0
[147] highr_0.11 dbplyr_2.5.0
[149] fastmap_1.2.0 rlang_1.1.4
[151] GlobalOptions_0.1.2 htmlwidgets_1.6.4
[153] UCSC.utils_1.0.0 shiny_1.9.1
[155] DelayedMatrixStats_1.26.0 farver_2.1.2
[157] jquerylib_0.1.4 zoo_1.8-12
[159] jsonlite_1.8.9 BiocParallel_1.38.0
[161] RCurl_1.98-1.14 BiocSingular_1.20.0
[163] magrittr_2.0.3 Formula_1.2-5
[165] scuttle_1.14.0 GenomeInfoDbData_1.2.12
[167] dotCall64_1.2 patchwork_1.3.0
[169] Rhdf5lib_1.26.0 munsell_0.5.1
[171] Rcpp_1.0.13 viridis_0.6.5
[173] reticulate_1.39.0 pROC_1.18.5
[175] alabaster.schemas_1.4.0 stringi_1.8.4
[177] zlibbioc_1.50.0 MASS_7.3-61
[179] AnnotationHub_3.12.0 plyr_1.8.9
[181] parallel_4.4.1 listenv_0.9.1
[183] ggrepel_0.9.6 scDblFinder_1.18.0
[185] deldir_2.0-4 Biostrings_2.72.1
[187] splines_4.4.1 tensor_1.5
[189] circlize_0.4.16 locfit_1.5-9.9
[191] igraph_2.1.1 spatstat.geom_3.3-3
[193] RcppHNSW_0.6.0 reshape2_1.4.4
[195] stats4_4.4.1 ScaledMatrix_1.12.0
[197] XML_3.99-0.17 BiocVersion_3.19.1
[199] evaluate_1.0.1 scran_1.32.0
[201] BiocManager_1.30.25 foreach_1.5.2
[203] httpuv_1.6.15 RANN_2.6.2
[205] tidyr_1.3.1 purrr_1.0.2
[207] polyclip_1.10-7 future_1.34.0
[209] clue_0.3-65 scattermore_1.2
[211] ggplot2_3.5.1 rsvd_1.0.5
[213] xtable_1.8-4 restfulr_0.0.15
[215] RSpectra_0.16-2 later_1.3.2
[217] viridisLite_0.4.2 tibble_3.2.1
[219] GenomicAlignments_1.40.0 beeswarm_0.4.0
[221] memoise_2.0.1 AnnotationDbi_1.66.0
[223] IRanges_2.38.1 cluster_2.1.6
[225] globals_0.16.3
LS0tCnRpdGxlOiAiPENFTlRFUj5FQkFJSSBuMSAyMDI0IDogU0lOR0xFIENFTEwgQU5BTFlTSVMgVFJBSU5JTkc8QlI+IDxCPlBSRVBST0NFU1NJTkcgKElJSSk8L0I+PEJSPkZpbHRlcmluZywgY2VsbCBjeWxlLCBjZWxsIGRvdWJsZXRzPC9DRU5URVI+IgpkYXRlOiAiMjAyNC0xMS0xNy4yMiIKYXV0aG9yOiAiRUJBSUkgbjEgc2NSTkFzZXEgVGVhbSIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6IAogICAgYmFja2dyb3VuZDogYmxhY2sKICAgIGZpZ19oZWlnaHQ6IDYKICAgIGZpZ193aWR0aDogOAogICAgaGlnaGxpZ2h0OiB0YW5nbyAgIyMgVGhlbWUgZm9yIHRoZSBjb2RlIGNodW5rcwogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlICAjIyBBZGRzIG51bWJlciB0byBoZWFkZXJzIChzZWN0aW9ucykKICAgIHRoZW1lOiBmbGF0bHkgICMjIENTUyB0aGVtZSBmb3IgdGhlIEhUTUwgcGFnZQogICAgdG9jOiB0cnVlICAjIyBBZGRzIGEgdGFibGUgb2YgY29udGVudAogICAgdG9jX2Zsb2F0OiAgIyMgVE9DIG9wdGlvbnMKICAgICAgY29sbGFwc2VkOiB0cnVlICAjIyBCeSBkZWZhdWx0LCB0aGUgVE9DIGlzIGZvbGRlZAogICAgICBzbW9vdGhfc2Nyb2xsOiB0cnVlICMjIFNtb290aCBzY3JvbGwgb2YgdGhlIEhUTUwgcGFnZQogICAgc2VsZl9jb250YWluZWQ6IHRydWUgIyMgSW5jbHVkZXMgYWxsIHBsb3RzL2ltYWdlcyB3aXRoaW4gdGhlIEhUTUwKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUgIyMgQWRkcyBhIGJ1dHRvbiB0byBkb3dubG9hZCB0aGUgUm1kCiAgICBjb2RlX2ZvbGRpbmc6IHNob3cKICAgIHRodW1ibmFpbHM6IGZhbHNlCiAgICBsaWdodGJveDogdHJ1ZQogICAgZmlnX2NhcHRpb246IGZhbHNlCiAgICBnYWxsZXJ5OiB0cnVlCiAgICB1c2VfYm9va2Rvd246IHRydWUKYWx3YXlzX2FsbG93X2h0bWw6IHRydWUgIyMgQWxsb3cgcGxhaW4gSFRNTCBjb2RlIGluIHRoZSBSbWQKZWRpdG9yX29wdGlvbnM6IAogIG1hcmtkb3duOiAKICAgIHdyYXA6IDcyCi0tLQoKPCEtLSBBZGQgdGhlIFJvc2NvZmYgYmFubmVyIC0tPgoKYGBge2NzcywgZWNobyA9IEZBTFNFfQpib2R5IHsKICBiYWNrZ3JvdW5kLWltYWdlOiB1cmwoJ2ViYWlpX2Jhbm5lci5wbmcnKTsKICBiYWNrZ3JvdW5kLXJlcGVhdDogbm8tcmVwZWF0OwogIGJhY2tncm91bmQtc2l6ZTogMTAwJTsKfQoKZGl2IHsKICBiYWNrZ3JvdW5kLWNvbG9yOiByZ2JhKDI1NSwgMjU1LCAyNTUsIDAuMzUpICAgLyogMzUlIG9wYXF1ZSB3aGl0ZSAqLzsKICBwYWRkaW5nOiAwLjI1ZW07Cn0KYGBgCgo8IS0tIEFsbG93cyB0byBoaWRlIHRoZSBUT0MgYnkgZGVmYXVsdCwgZGlzcGxheSBpdCB3aXRoIGEgYnV0dG9uLCBtb3ZlIGl0IHRvIHRoZSByaWdodCBvciBsZWZ0IG9mIHRoZSBwYWdlIC0tPgoKYHIgSG1pc2M6OmhpZGluZ1RPQyhidXR0b25MYWJlbCA9ICdTaG93IFRPQycsIGhpZGRlbiA9IFRSVUUsIHRvY1NpZGUgPSAnbGVmdCcsIGJ1dHRvblNpZGU9J2xlZnQnLCBwb3NDb2xsYXBzZSA9ICdtYXJnaW4nLCBsZXZlbHMgPSAzKWAKCmBgYHtyIGtuaXRfc2V0dXAsIGVjaG8gPSBGQUxTRX0KIyBvcHRpb25zKHdpZHRoID0gNjApOwprbml0cjo6b3B0c19jaHVuayRzZXQoCiAgZWNobyA9IFRSVUUsICAgICAgICAjIFByaW50IHRoZSBjb2RlCiAgZXZhbCA9IFRSVUUsICAgICAgICAjIFJ1biBjb21tYW5kIGxpbmVzCiAgbWVzc2FnZSA9IEZBTFNFLCAgICAjIFByaW50IG1lc3NhZ2VzCiAgcHJvbXB0ID0gRkFMU0UsICAgICAjIERvIG5vdCBkaXNwbGF5IHByb21wdAogIGNvbW1lbnQgPSBOQSwgICAgICAgIyBObyBjb21tZW50cyBvbiB0aGlzIHNlY3Rpb24KICB3YXJuaW5nID0gRkFMU0UsICAgICMgRGlzcGxheSB3YXJuaW5ncwogIHRpZHkgPSBGQUxTRSwKICBmaWcuYWxpZ24gPSAiY2VudGVyIiwKICBmaWcuYWxpZ249ImNlbnRlciIsIAogICMgcmVzdWx0cyA9ICdoaWRlJwogIHdpZHRoID0gMTAwICAgICAgICMgTnVtYmVyIG9mIGNoYXJhY3RlcnMgcGVyIGxpbmUKKQpgYGAKCmBgYHtyIGtuaXRfaG9vaywgZWNobyA9IEZBTFNFfQpob29rcyA9IGtuaXRyOjprbml0X2hvb2tzJGdldCgpCmhvb2tfZm9sZGFibGUgPSBmdW5jdGlvbih0eXBlKSB7CiAgZm9yY2UodHlwZSkKICBmdW5jdGlvbih4LCBvcHRpb25zKSB7CiAgICByZXMgPSBob29rc1tbdHlwZV1dKHgsIG9wdGlvbnMpCiAgICAKICAgIGlmIChpc0ZBTFNFKG9wdGlvbnNbW3Bhc3RlMCgiZm9sZC4iLCB0eXBlKV1dKSkgcmV0dXJuKHJlcykKICAgIAogICAgcGFzdGUwKAogICAgICAiPGRldGFpbHM+PHN1bW1hcnk+U2hvdyAiLCB0eXBlLCAiPC9zdW1tYXJ5PlxuXG4iLAogICAgICByZXMsCiAgICAgICJcblxuPC9kZXRhaWxzPiIKICAgICkKICB9Cn0Ka25pdHI6OmtuaXRfaG9va3Mkc2V0KAogIG91dHB1dCA9IGhvb2tfZm9sZGFibGUoIm91dHB1dCIpLAogIHBsb3QgPSBob29rX2ZvbGRhYmxlKCJwbG90IikKKQpgYGAKCjwhLS0gQ1NTIHRvIGNvbG9yIGNodW5rcyBhbmQgb3V0cHV0cyAtLT4KCmBgYHtjc3MsIGVjaG89RkFMU0V9Ci5ub3RydW4gewogIGJhY2tncm91bmQtY29sb3I6IGxpZ2h0Z3JleSAhaW1wb3J0YW50OwogIGJvcmRlcjogM3B4IHNvbGlkIGJsYWNrICFpbXBvcnRhbnQ7Cn0KLm5vdHJ1bm8gewogIGJhY2tncm91bmQtY29sb3I6IGxpZ2h0Z3JleSAhaW1wb3J0YW50OwogIGNvbG9yIDogYmxhY2sgIWltcG9ydGFudDsKfQoucXVlc3Rpb24gewogIGJhY2tncm91bmQtY29sb3I6IGFxdWFtYXJpbmUgIWltcG9ydGFudDsKICBjb2xvciA6IGJsYWNrICFpbXBvcnRhbnQ7CiAgYm9yZGVyOiAzcHggc29saWQgbGltZWdyZWVuICFpbXBvcnRhbnQ7Cn0KLnF1ZXN0aW9ubyB7CiAgYmFja2dyb3VuZC1jb2xvcjogYXF1YW1hcmluZSAhaW1wb3J0YW50OwogIGNvbG9yIDogYmxhY2sgIWltcG9ydGFudDsKfQouYW5zd2VyIHsKICBiYWNrZ3JvdW5kLWNvbG9yOiBuYXZham93aGl0ZSAhaW1wb3J0YW50OwogIGJvcmRlcjogM3B4IHNvbGlkIGJyb3duICFpbXBvcnRhbnQ7Cn0KLmFuc3dlcm8gewogIGJhY2tncm91bmQtY29sb3I6IG5hdmFqb3doaXRlICFpbXBvcnRhbnQ7CiAgY29sb3IgOiBibGFjayAhaW1wb3J0YW50Owp9Ci5iZXlvbmQgewogIGJhY2tncm91bmQtY29sb3I6IHZpb2xldCAhaW1wb3J0YW50OwogIGJvcmRlcjogM3B4IHNvbGlkIHB1cnBsZSAhaW1wb3J0YW50Owp9Ci5iZXlvbmRvIHsKICBiYWNrZ3JvdW5kLWNvbG9yOiB2aW9sZXQgIWltcG9ydGFudDsKICBjb2xvciA6IGJsYWNrICFpbXBvcnRhbnQ7Cn0KYGBgCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBQUkVBTUJMRQoKIyMgUHVycG9zZSBvZiB0aGlzIHNlc3Npb24KClRoaXMgZmlsZSBkZXNjcmliZXMgdGhlIGRpZmZlcmVudCBzdGVwcyB0byBwZXJmb3JtIHRoZSAqKnRoaXJkIHBhcnQqKiBvZgp0aGUgc2luZ2xlIGNlbGwgUk5Bc2VxIGRhdGEgYW5hbHlzaXMgdHJhaW5pbmcgY291cnNlIGZvciB0aGUgKipFQkFJSSBuMQoyMDI0KiosIGNvdmVyaW5nIHRoZXNlIHN0ZXBzIDoKCi0gICAqKkZpbHRlcmluZyoqIG9mIGJhZCBjZWxscyBhbmQgZXh0cmVtZSBsb3ctZXhwcmVzc2lvbiBmZWF0dXJlcwoKLSAgIEVzdGltYXRpb24gb2YgdGhlICoqY2VsbCBjeWNsZSoqIHBoYXNlCgotICAgSWRlbnRpZmljYXRpb24gYW5kIHJlbW92YWwgb2YgKipjZWxsIGRvdWJsZXRzKioKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIFN0YXJ0IFJzdHVkaW8KCi0gICBVc2luZyB0aGUgW09wZW5PbkRlbWFuZCBjaGVhdAogICAgc2hlZXRdKGh0dHBzOi8vaWZiLWVsaXhpcmZyLmdpdGh1Yi5pby9FQkFJSS8yMDIzL2ViYWlpbjEvU2luZ2xlQ2VsbC8yMDI0X1REX09wZW5PbkRlbWFuZC5odG1sKXt0YXJnZXQ9Il9ibGFuayJ9LAogICAgY29ubmVjdCB0byB0aGUgW09wZW5PbkRlbWFuZAogICAgcG9ydGFsXShodHRwczovL29uZGVtYW5kLmNsdXN0ZXIuZnJhbmNlLWJpb2luZm9ybWF0aXF1ZS5mcil7dGFyZ2V0PSJfYmxhbmsifSBhbmQKICAgICoqY3JlYXRlIGEgUnN0dWRpbyBzZXNzaW9uKiogd2l0aCB0aGUgcmlnaHQgcmVzb3VyY2UgcmVxdWlyZW1lbnRzLgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgV2FybS11cAoKLSAgIFdlIHNldCAqKmNvbW1vbiBwYXJhbWV0ZXJzKiogd2Ugd2lsbCB1c2UgdGhyb3VnaG91dCB0aGlzIHNlc3Npb24gOgoKYGBge3Igc2V0cGFyYW19CiMjIFNldCB5b3VyIHByb2plY3QgbmFtZQpwcm9qZWN0X25hbWUgPC0gImViYWlpX3NjX3RlYWNoZXJzIiAgIyBEbyBub3QgY29weS1wYXN0ZSB0aGlzICEgSXQncyBNWSBwcm9qZWN0ICEgUHV0IHlvdXJzICEhCgojIyBTZWVkIGZvciB0aGUgUk5HCm15X3NlZWQgPC0gMTMzN0wKCiMjIE1pbmltdW0gY2VsbHMgd2l0aCBjb3VudHMgdG8ga2VlcCBhIGZlYXR1cmUKbWluX2NlbGxzIDwtIDUKYGBgCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBQcmVwYXJlIHRoZSBkYXRhIHN0cnVjdHVyZQoKV2Ugd2lsbCBkbyB0aGUgc2FtZSBhcyBmb3IgZm9ybWVyIHN0ZXBzLCBqdXN0IGNoYW5naW5nIHRoZSBzZXNzaW9uIG5hbWUKOgoKIyMgTWFpbiBkaXJlY3RvcnkKCmBgYHtyIG1haW5kaXIsIGZvbGQub3V0cHV0ID0gRkFMU0V9CiMjIFByZXBhcmluZyB0aGUgcGF0aApURF9kaXIgPC0gcGFzdGUwKCIvc2hhcmVkL3Byb2plY3RzLyIsIHByb2plY3RfbmFtZSwgIi9TQ19URCIpCgojIyBDcmVhdGluZyB0aGUgcm9vdCBkaXJlY3RvcnkKZGlyLmNyZWF0ZShwYXRoID0gVERfZGlyLCByZWN1cnNpdmUgPSBUUlVFKQoKIyMgUHJpbnQgdGhlIHJvb3QgZGlyZWN0b3J5IG9uLXNjcmVlbgpwcmludChURF9kaXIpCmBgYAoKIyMgQ3VycmVudCBzZXNzaW9uCgpgYGB7ciBzZXNzaW9uZGlyLCBmb2xkLm91dHB1dCA9IEZBTFNFfQojIyBDcmVhdGluZyB0aGUgc2Vzc2lvbiAoUHJlcHJvYy4zKSBkaXJlY3RvcnkKc2Vzc2lvbl9kaXIgPC0gcGFzdGUwKFREX2RpciwgIi8wM19QcmVwcm9jLjMiKQpkaXIuY3JlYXRlKHBhdGggPSBzZXNzaW9uX2RpciwgcmVjdXJzaXZlID0gVFJVRSkKCiMjIFByaW50IHRoZSBzZXNzaW9uIGRpcmVjdG9yeSBvbi1zY3JlZW4KcHJpbnQoc2Vzc2lvbl9kaXIpCmBgYAoKIyMgSW5wdXQgZGlyZWN0b3J5CgpgYGB7ciBpbmRpciwgZm9sZC5vdXRwdXQgPSBGQUxTRX0KIyMgQ3JlYXRpbmcgdGhlIElOUFVUIGRhdGEgZGlyZWN0b3J5CmlucHV0X2RpciA8LSBwYXN0ZTAoc2Vzc2lvbl9kaXIsICIvREFUQSIpCmRpci5jcmVhdGUocGF0aCA9IGlucHV0X2RpciwgcmVjdXJzaXZlID0gVFJVRSkKCiMjIFByaW50IHRoZSBpbnB1dCBkaXJlY3Rvcnkgb24tc2NyZWVuCnByaW50KGlucHV0X2RpcikKYGBgCgojIyBHZW5lbGlzdHMgZGlyZWN0b3J5CgpgYGB7ciByZXNkaXIsIGZvbGQub3V0cHV0ID0gRkFMU0V9CnJlc19kaXIgPC0gcGFzdGUwKFREX2RpciwgIi9SZXNvdXJjZXMiKQpnbGlzdF9kaXIgPC0gcGFzdGUwKHJlc19kaXIsICIvR2VuZWxpc3RzIikKCiMjIFByaW50IHRoZSByZXNvdXJjZXMgZGlyZWN0b3J5IG9uLXNjcmVlbgpwcmludChnbGlzdF9kaXIpCmBgYAoKIyMgT3V0cHV0IGRpcmVjdG9yeQoKYGBge3Igb3V0ZGlyLCBmb2xkLm91dHB1dCA9IEZBTFNFfQojIyBDcmVhdGluZyB0aGUgT1VUUFVUIGRhdGEgZGlyZWN0b3J5Cm91dHB1dF9kaXIgPC0gcGFzdGUwKHNlc3Npb25fZGlyLCAiL1JFU1VMVFMiKQpkaXIuY3JlYXRlKHBhdGggPSBvdXRwdXRfZGlyLCByZWN1cnNpdmUgPSBUUlVFKQoKIyMgUHJpbnQgdGhlIG91dHB1dCBkaXJlY3Rvcnkgb24tc2NyZWVuCnByaW50KG91dHB1dF9kaXIpCmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgUmVsb2FkIHRoZSBTZXVyYXQgT2JqZWN0CgotICAgV2UgY2FuIHJlbG9hZCB0aGUgb2JqZWN0IHdlIHNhdmVkIGF0IHRoZSBmb3JtZXIgc3RlcAoKYGBge3IgZGF0YWxvYWR9CiMjIFRoZSBsYXRlc3QgU2V1cmF0IG9iamVjdCBzYXZlZCBhcyBSRFMgKG5hbWUpCnNvYmpfZmlsZSA8LSAiMDJfVEQzQV9TNV9NZXRyaWNzLkJpb18zMTA1My40NTg3LlJEUyIKCiMjIFRoZSBsYXRlc3QgU2V1cmF0IG9iamVjdCBzYXZlZCBhcyBSRFMgKGZ1bGwgcGF0aCkKc29ial9wYXRoIDwtIHBhc3RlMChURF9kaXIsIAogICAgICAgICAgICAgICAgICAgICIvMDJfUHJlcHJvYy4yL1JFU1VMVFMvIiwKICAgICAgICAgICAgICAgICAgICBzb2JqX2ZpbGUpCgpmb3JjZSA8LSBGQUxTRSAgIyMgVG8gZm9yY2UgYSByZS1kb3dubG9hZCBvZiBhIFplbm9kby1ob3N0ZWQgYmFja3VwCmxvY2FsIDwtIEZBTFNFICAjIyBUbyBmb3JjZSBhIGxvYWRpbmcgZnJvbSBhIGxvY2FsIGJhY2t1cAoKIyMgSW4gY2FzZSBvZiBlcnJvci9sb3N0IGRhdGEgOiBmb3JjZSBhIHJlbG9hZCBmcm9tIGEgWmVub2RvIGJhY2t1cCByZXBvc2l0b3J5CmlmKGZvcmNlKSB7CiAgemVuX2lkIDwtICIxNDAzNTI5MyIKICB6ZW5fYmFja3VwX2ZpbGUgPC0gcGFzdGUwKCJodHRwczovL3plbm9kby5vcmcvcmVjb3Jkcy8iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgemVuX2lkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIi9maWxlcy8iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgc29ial9maWxlKQogIGRvd25sb2FkLmZpbGUodXJsID0gemVuX2JhY2t1cF9maWxlLAogICAgICAgICAgICAgICAgZGVzdGZpbGUgPSBzb2JqX3BhdGgpCn0KCiMjIEluIGNhc2Ugb2YgZXJyb3IvbG9zdCBkYXRhIDogZm9yY2UgYSByZWxvYWQgZnJvbSBhIGxvY2FsIGJhY2t1cCByZXBvc2l0b3J5CmlmKGxvY2FsKSB7CiAgc29ial9wYXRoIDwtIHBhc3RlMCgKICAgICIvc2hhcmVkL3Byb2plY3RzLzI0MjJfZWJhaWlfbjEvYXRlbGllcl9zY3JuYXNlcS9URC9CQUNLVVAvUkRTLyIsCiAgICBzb2JqX2ZpbGUpCn0KCiMjIExvYWQgdGhlIG9iamVjdApzb2JqIDwtIHJlYWRSRFMoZmlsZSA9IHNvYmpfcGF0aCkKYGBgCgo8YnI+PGJyPgoKTm93LCB3ZSBmaW5hbGx5IGhhdmUgYWxsIHRoZSBtZXRyaWNzIGFuZCBiaWFzIHNvdXJjZXMgd2UgY291bGQKdXNlLCBzbyB3ZSBjYW4gYWN0dWFsbHkgZ2V0IHRvIHRoZSBmaWx0ZXJpbmcgc3RlcC4KCiMgRmlsdGVyaW5nCgojIyBGZWF0dXJlcwoKLSAgIEFzIGFscmVhZHkgZXhwbGFpbmVkLCB0aGUgKipzcGFyc2l0eSoqIG9mIHRoZSBjb3VudCBtYXRyaXggaXMgaGlnaCwgX3ZlcnlfCmhpZ2guIAoKLSAgIFRvIHRoZSBwb2ludCB0aGF0IHRoZXJlIHByb2JhYmx5IGFyZSBzb21lIGZlYXR1cmVzIHRoYXQgaGF2ZSAqKnNvCmxvdyBleHByZXNzaW9uKiogbGV2ZWwgdGhhdCB0aGV5IHdlcmUgb25seSBjb3VudGVkIGluICoqdmVyeSBmZXcgY2VsbHMqKi4gQXMKc3VjaCwgdGhleSBoYXZlIGFic29sdXRlbHkgKipubyBjaGFuY2UqKiB0byBjaGFyYWN0ZXJpemUgYW55IGNlbGwgdHlwZSwgbm9yCmhhcmJvciBzb21lIHZhcmlhdGlvbiBiZXR3ZWVuIGRpZmZlcmVudCBjZWxsIHR5cGVzLgoKLSAgIEFzIHN1Y2gsIHdlIHNob3VsZCAqKmRpc2NhcmQqKiB0aGVzZSBmZWF0dXJlcwoKPGJyPgoKV2hhdCBhcmUgdGhlIGFjdHVhbCBkaW1lbnNpb25zIG9mIG91ciBleHByZXNzaW9uIGRhdGEgPwoKYGBge3Igc2V1ZGltMX0KIyMgRGltZW5zaW9ucyBvZiBvdXIgU2V1cmF0IG9iamVjdApkaW0oc29iaikKYGBgCgo8YnI+CgpXZSB3YW50IHRvIHJlbW92ZSB0aGUgZmVhdHVyZXMgdGhhdCBhcmUgZXhwcmVzc2VkIGluIGxlc3MgdGhhbiAqKmByIG1pbl9jZWxsc2AqKgpjZWxscy4KCkZpcnN0LCB3ZSBxdWFudGlmeSwgZm9yIGVhY2ggZmVhdHVyZSwgdGhlIG51bWJlciBvZiBjZWxscyB3aXRoIDAgY291bnQuCgpgYGB7ciBuZmVhdGRpZmZ9CiMjIENvbXB1dGUgdGhlIGFtb3VudCBvZiAwcyBwZXIgZmVhdHVyZQpuRmVhdF96ZXJvIDwtIHNwYXJzZU1hdHJpeFN0YXRzOjpyb3dDb3VudHMoCiAgeCA9IFNldXJhdE9iamVjdDo6R2V0QXNzYXlEYXRhKAogICAgb2JqZWN0ID0gc29iaiwgCiAgICBhc3NheSA9ICJSTkEiLCAKICAgIGxheWVyID0gImNvdW50cyIpLCAKICB2YWx1ZSA9IDApCgojIyBJbnZlcnNpb24gOiBjb21wdXRpbmcgdGhlIG51bWJlciBvZiBjZWxscyB3aXRoIGF0IGxlYXN0IG9uZSBjb3VudCwgcGVyIGZlYXR1cmUKbkZlYXRfbm9uemVybyA8LSBuY29sKHNvYmopIC0gbkZlYXRfemVybwoKIyMgSWRlbnRpZnkgdGhvc2Ugd2l0aCBhdCBsZWFzdCA1IGNlbGxzIHdpdGggZXhwcmVzc2lvbgpuRmVhdF9rZWVwIDwtIG5GZWF0X25vbnplcm8gPj0gbWluX2NlbGxzCgojIyBRdWFudGlmeSB0aGUgc2VsZWN0ZWQgb25lcwp0YWJsZShuRmVhdF9rZWVwKQpgYGAKCjxicj4KCkFuZCBub3cgd2UgY2FuICoqZGlzY2FyZCoqIGZlYXR1cmVzIGV4cHJlc3NlZCBpbiBsZXNzIHRoYW4gYHIgbWluX2NlbGxzYCBjZWxscyA6CgpgYGB7ciBwcmVmZWF0ZmlsdHZpeiwgY2xhc3Muc291cmNlPSJub3RydW4iLCBjbGFzcy5vdXRwdXQ9Im5vdHJ1bm8ifQpmaWx0YzVfcHJlIDwtIFNDLmhlbHBlcjo6UW5EX3Zpeihzb2JqID0gc29iaiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuX3Bsb3QgPSBUUlVFKQpgYGAKCjxicj4KCmBgYHtyIGZlYXRzZWx9CgojIyBSZXN0cmljdCB0aGUgU2V1cmF0IG9iamVjdApzb2JqIDwtIHNvYmpbbkZlYXRfa2VlcCxdCgpgYGAKCjxicj4KCldoYXQgYXJlIHRoZSBTZXVyYXQgb2JqZWN0IGRpbWVuc2lvbnMsIG5vdyA/CgpgYGB7ciBzZXVkaW0yLCBjbGFzcy5zb3VyY2U9Im5vdHJ1biIsIGNsYXNzLm91dHB1dD0ibm90cnVubyJ9CmRpbShzb2JqKQpgYGAKCjxicj4KCldlIGNhbiAqKnZpc3VhbGl6ZSB0aGUgY2VsbCBzcGFjZSoqIGFmdGVyIHRoaXMgZmVhdHVyZXMgZmlsdGVyaW5nCgpgYGB7ciBwb3N0ZmVhdGZpbHR2aXp9CmZpbHRjNV9wb3N0IDwtIFNDLmhlbHBlcjo6UW5EX3Zpeihzb2JqID0gc29iaiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm5fcGxvdCA9IFRSVUUpCmBgYAoKPGJyPgoKTWVyZ2luZyBwbG90cyBmb3IgZWFzZSBvZiB2aXN1YWxpemF0aW9uIDoKCmBgYHtyIGM1X3B3LCBmaWcud2lkdGggPSAxNSwgZmlnLmhlaWdodCA9IDYsIGNsYXNzLnNvdXJjZT1jKCJmb2xkLWhpZGUiLCAibm90cnVuIiksIGNsYXNzLm91dHB1dD0ibm90cnVubyJ9CiMjIFVzaW5nIHRoZSBwYXRjaHdvcmsgcGFja2FnZSB0byBtZXJnZSBwbG90cyAoYW5kIGdncGxvdDIgdG8gYWRkIHRpdGxlcykKcGF0Y2h3b3JrOjp3cmFwX3Bsb3RzKAogIGxpc3QoCiAgICBmaWx0YzVfcHJlICYgZ2dwbG90Mjo6Z2d0aXRsZShsYWJlbCA9ICJCRUZPUkUiKSwKICAgIGZpbHRjNV9wb3N0ICYgZ2dwbG90Mjo6Z2d0aXRsZShsYWJlbCA9ICJBRlRFUiIpKSwgCiAgbnJvdyA9IDEpCmBgYAoKPGJyPgoKLSAgICoqUXVlc3Rpb24qKiA6IAogICAgCiAgICBgYGB7ciBxX21pbmNlbGwsIGV2YWwgPSBGQUxTRSwgY2xhc3Muc291cmNlPSJxdWVzdGlvbiIsIGV2YWwgPSBGQUxTRX0KICAgIERvIHlvdSBzZWUgYW55IGRpZmZlcmVuY2Ugd2hlbiBjb21wYXJpbmcgYmVmb3JlIHZzIGFmdGVyIGZlYXR1cmVzIGZpbHRlcmluZyA/CiAgICBgYGAKICAgIAogICAgPGJyPgogICAgCiAgICBgYGB7ciBhX21pbmNlbGwsIGNsYXNzLnNvdXJjZSA9IGMoImZvbGQtaGlkZSIsICJhbnN3ZXIiKSwgZXZhbCA9IEZBTFNFfQogICAgIyMgLiBOb3QgbXVjaCBjaGFuZ2VkLCBtYXliZSBjZWxsIGdyb3VwcyBzZWVtIGEgYml0IG1vcmUgY29uZGVuc2VkCiAgICAjIwogICAgIyMgLiBUaGlzIGlzIGV4cGVjdGVkLCBhcyB3ZSByZW1vdmVkIGZlYXR1cmVzIHdpdGggYWxtb3N0IG5vIGV4cHJlc3Npb24KICAgIGBgYAogICAgCjxicj48YnI+CgojIyBDZWxscwoKV2UgYXJlIG5vdyBhYmxlIHRvICoqYXBwbHkgYWxsIHRoZSBmaWx0ZXJpbmcgc3RyYXRlZ2llcyoqIHdlIGVzdGFibGlzaGVkIGZvcgplYWNoIFFDIG1ldHJpYy4KCi0gICBJZGVudGlmeSAiZ29vZCIgY2VsbHMKCmBgYHtyIGJja2VlcH0KIyMgSWRlbnRpZnkgdGhlIGNlbGxzIHRvIGtlZXAKYmNfa2VlcCA8LSAgc29iaiRuRmVhdHVyZV9STkFfaW5fcmFuZ2UgJiAKICAgICAgICAgICAgc29iaiRuQ291bnRfUk5BX2luX3JhbmdlICYgCiAgICAgICAgICAgIHNvYmokcGVyY2VudF9tdF9pbl9yYW5nZSAmIAogICAgICAgICAgICBzb2JqJHBlcmNlbnRfcmJfaW5fcmFuZ2UgJiAKICAgICAgICAgICAgc29iaiRwZXJjZW50X3N0X2luX3JhbmdlCgojIyBDb250ZW5nZW5jeQp0YWJsZShiY19rZWVwKQpgYGAKCjxicj4KCi0gICBWaXN1YWxpemUgbWV0cmljcyBmaWx0ZXJpbmcgZWZmZWN0IChhcyBhbiB1cHNldC1wbG90KQoKICAgIGBgYHtyIGJjdXBzZXQsIGNsYXNzLnNvdXJjZT0ibm90cnVuIiwgY2xhc3Mub3V0cHV0PSJub3RydW5vIn0KICAgICMjIENyZWF0ZSBhIGxpc3Qgb2YgYWxsIGNlbGxzIGZpbHRlcmVkIG91IGZvciBlYWNoIGNyaXRlcmlvbgogICAgdXBfbGlzdCA8LWxpc3QoCiAgICAgICJuRmVhdHVyZSIgPSBjb2xuYW1lcyhzb2JqKVshc29iaiRuRmVhdHVyZV9STkFfaW5fcmFuZ2VdLAogICAgICAibkNvdW50IiA9IGNvbG5hbWVzKHNvYmopWyFzb2JqJG5Db3VudF9STkFfaW5fcmFuZ2VdLAogICAgICAiJU1JVE8iID0gY29sbmFtZXMoc29iailbIXNvYmokcGVyY2VudF9tdF9pbl9yYW5nZV0sCiAgICAgICIlUklCTyIgPSBjb2xuYW1lcyhzb2JqKVshc29iaiRwZXJjZW50X3JiX2luX3JhbmdlXSwKICAgICAgIiVTVFJFU1MiID0gY29sbmFtZXMoc29iailbIXNvYmokcGVyY2VudF9zdF9pbl9yYW5nZV0pCiAgICAKICAgICMjIENyZWF0ZSBhbiB1cHNldC1wbG90CiAgICBVcFNldFI6OnVwc2V0KGRhdGEgPSBVcFNldFI6OmZyb21MaXN0KHVwX2xpc3QpLCAKICAgICAgICAgICAgICAgICAgbmludGVyc2VjdHMgPSBOQSwgCiAgICAgICAgICAgICAgICAgIHNldHMgPSByZXYobmFtZXModXBfbGlzdCkpLAogICAgICAgICAgICAgICAgICBrZWVwLm9yZGVyID0gVFJVRSwKICAgICAgICAgICAgICAgICAgb3JkZXIuYnkgPSAiZnJlcSIpCiAgICBgYGAKCjxicj4KCi0gICBBcHBseSB0aGUgZmlsdGVyCgogICAgYGBge3IgY2VsbGZpbHRfMX0KICAgICMjIFNldXJhdCBvYmplY3QgQkVGT1JFIGNlbGwgZmlsdGVyaW5nCiAgICBkaW0oc29iaikKICAgIGBgYAoKICAgIGBgYHtyIGNlbGxmaWx0XzJ9CiAgICAjIyBBcHBseSBjZWxsIGZpbHRlcmluZwogICAgc29iaiA8LSBzdWJzZXQoeCA9IHNvYmosIGNlbGxzID0gY29sbmFtZXMoc29iailbYmNfa2VlcF0pCiAgICAKICAgICMjIFNldXJhdCBvYmplY3QgQUZURVIgY2VsbCBmaWx0ZXJpbmcKICAgIGRpbShzb2JqKQogICAgYGBgCgo8YnI+CgpXZSBjYW4gKip2aXN1YWxpemUgdGhlIGNlbGwgc3BhY2UqKiBzaW5jZSB0aGlzIGNlbGwgZmlsdGVyaW5nCgpgYGB7ciBwb3N0Y2VsbGZpbHR2aXp9ClNDLmhlbHBlcjo6UW5EX3Zpeihzb2JqID0gc29iaikKYGBgCgo8YnI+CgotICAgKipRdWVzdGlvbioqCiAgICAKICAgIGBgYHtyIHFfdml6ZmlsdCwgZXZhbCA9IEZBTFNFLCBjbGFzcy5zb3VyY2U9InF1ZXN0aW9uIiwgZXZhbCA9IEZBTFNFfQogICAgRG8geW91IHNlZSBhbnkgZGlmZmVyZW5jZSB3aGVuIGNvbXBhcmluZyBiZWZvcmUgdnMgYWZ0ZXIgZmVhdHVyZXMgZmlsdGVyaW5nID8KICAgIGBgYAogICAgCiAgICA8YnI+CiAgICAKICAgIGBgYHtyIHFfcG9zdGNlbGxmaWx0LCBjbGFzcy5zb3VyY2UgPSBjKCJmb2xkLWhpZGUiLCAiYW5zd2VyIiksIGV2YWwgPSBGQUxTRX0KICAgICMjIC4gTm90IG11Y2ggY2hhbmdlZCBhcyB3ZWxsICh3ZSBkaWQgbm90IGRpc2NhcmQgbWFueSBjZWxscykKICAgICMjCiAgICAjIyAuIFRoZSBiaWdnZXN0IGNsdXN0ZXIgc3RydWN0dXJlIHNlZW1zIG1vcmUgZGVmaW5lZAogICAgYGBgCiAgICAKICAgIDxicj4KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIFNhdmUgdGhlIFNldXJhdCBvYmplY3QKCldlIHdpbGwgKipzYXZlIG91ciBTZXVyYXQgb2JqZWN0KiogdGhhdCBub3cgY29udGFpbnMgKipmaWx0ZXJlZCBjZWxscyBhbmQKZmVhdHVyZXMqKiA6CgpgYGB7ciBzYXZlcmRzMiwgZm9sZC5vdXRwdXQgPSBGQUxTRX0KIyMgU2F2ZSBvdXIgU2V1cmF0IG9iamVjdCAocmljaCBuYW1pbmcpCm91dF9uYW1lIDwtIHBhc3RlMCgKICAgICAgICAgIG91dHB1dF9kaXIsICIvIiwgcGFzdGUoCiAgICAgICAgICAgIGMoIjAzIiwgU2V1cmF0OjpQcm9qZWN0KHNvYmopLCAiUzUiLCAKICAgICAgICAgICAgICAiTWV0cmljcy5GaWx0ZXJlZCIsIHBhc3RlKAogICAgICAgICAgICAgICAgZGltKHNvYmopLCAKICAgICAgICAgICAgICAgIGNvbGxhcHNlID0gJy4nCiAgICAgICAgICAgICAgKQogICAgICAgICAgICApLCBjb2xsYXBzZSA9ICJfIiksCiAgICAgICAgICAgICIuUkRTIikKCiMjIENoZWNrCnByaW50KG91dF9uYW1lKQoKIyMgV3JpdGUgb24gZGlzawpzYXZlUkRTKG9iamVjdCA9IHNvYmosIAogICAgICAgIGZpbGUgPSBvdXRfbmFtZSkKYGBgCjxicj4KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIENlbGwgY3ljbGUgc2NvcmVzCgotICAgV2UgYXJlIGN1cnJlbnRseSBhbmFseXppbmcgKippbmRlcGVuZGVudCoqIHByb2ZpbGVzIGZyb20gICoqaXNvbGF0ZWQqKiBjZWxscywgZnJvbSBhIHNhbXBsZSBkaXNzb2NpYXRpb24KCi0gICBBcyBzdWNoLCBjZWxscyB3ZXJlIG1vc3QgcHJvYmFibHkgKipub3Qgc3luY2hyb25pemVkKiosIHRodXMgdGhlIGVmZmVjdCBvZiB0aGVpciAqKmNlbGwgY3ljbGUgc3RhdGUqKiBvbiBnZW5lcyBleHByZXNzaW9uIG1heSBiZSBzdHJvbmcsIHRvIHRoZSBwb2ludCB0aGF0IGl0IGNhbiAqKmJpYXMgdGhlIGRhdGEqKiAoaWUsIG1hc2sgc29tZSBsb3dlciBhbXBsaXR1ZGUgYmlvbG9naWNhbCB2YXJpYXRpb24pLgoKLSAgIEluIG9yZGVyIHRvICoqYXNzZXNzKiogKGFuZCBtYXliZSwgX3JlbW92ZV8pIHRoaXMgYmlhcywgd2UgaGF2ZSB0byAqKnF1YW50aWZ5KiogaXQuCgotICAgV2Ugd2lsbCBwZXJmb3JtIHRoaXMgZXN0aW1hdGlvbiB0aGFua3MgdG8gaGV1cmlzdGljcyBiYXNlZCBvbiAqKmtub3dsZWRnZSoqIDogU2V1cmF0IGluY2x1ZGVzIGEgbWV0aG9kIHRoYXQgZXZhbHVhdGVzIHRoZSBjZWxsIGN5Y2xlIHBoYXNlIG9mIGNlbGxzIHRocm91Z2ggKipzY29yZXMgZm9yIHRoZSBTIGFuZCBHMk0gcGhhc2VzKiosIGVhY2ggYmFzZWQgb24gcGhhc2Utc3BlY2lmaWMgKipnZW5lIHNpZ25hdHVyZXMqKi4KCi0gICBGb3IgdGhpcyBzdGVwLCB3ZSB3aWxsIHVzZSBhZGRpdGlvbmFsIGdlbmUgbGlzdHMgZnJvbSBrbm93bGVkZ2UKICAgIChjZWxsIGN5Y2xlIHBoYXNlKSwgaG9zdGVkIGluIGEgWmVub2RvIHJlc3Bvc2l0b3J5IChJZCA6CiAgICBbMTQwMzczNTVdKGh0dHBzOi8vemVub2RvLm9yZy9yZWNvcmRzLzE0MDM3MzU1ICJaZW5vZG8gZ2VuZSBsaXN0cyIpe3RhcmdldD0iX2JsYW5rIn0pCgojIyBEb3dubG9hZCBnZW5lIGxpc3RzCgotICAgV2Ugd2lsbCBkaXJlY3RseSByZXRyaWV2ZSBkYXRhIGZyb20gWmVub2RvIHRvIHlvdXIgYGlucHV0X2RpcmAgOgoKICAgIGBgYHtyIGRsemVubWF0Mn0KICAgICMjIFplbm9kbyBJRAogICAgemVuX2lkIDwtICcxNDEwMTUwNicKICAgIAogICAgIyMjIE5hbWVkIGZpbGVzICh3aWxsIGJlIHVzZWQgbGF0ZXIgb24gISkKICAgIGNjX2ZpbGUgPC0gIm11c19tdXNjdWx1c19TZXVyYXRfY2MuZ2VuZXNfMjAxOTEwMzEucmRzIgogICAgCiAgICAjIyBGaWxlbmFtZShzKSB0byByZXRyaWV2ZQogICAgdG9nZXRfZmlsZXMgPC0gYyhjY19maWxlKQogICAgCiAgICAjIyBGb2xkZXIgdG8gc3RvcmUgcmV0cmlldmVkIGZpbGVzCiAgICBsb2NhbF9mb2xkZXIgPC0gZ2xpc3RfZGlyCiAgICAKICAgICMjIFVzZSBsb2NhbCBiYWNrdXAgPwogICAgYmFja3VwIDwtIEZBTFNFCiAgICBpZihiYWNrdXApIG1lc3NhZ2UoIlVzaW5nIGxvY2FsIGJhY2t1cCAhIikKICAgIAogICAgIyMgRm9yY2UgZG93bmxvYWQgPwogICAgZm9yY2UgPC0gRkFMU0UKICAgIGlmKGZvcmNlKSBtZXNzYWdlKCJGb3JjaW5nIChyZSlkb3dubG9hZCAhIikKICAgIAogICAgIyMjIERlZmluZSByZW1vdGUgZm9sZGVyCiAgICByZW1vdGVfZm9sZGVyIDwtIGlmIChiYWNrdXApIHsKICAgICAgIi9zaGFyZWQvcHJvamVjdHMvMjQyMl9lYmFpaV9uMS9hdGVsaWVyX3Njcm5hc2VxL1REL1JFU09VUkNFUy9HRU5FTElTVFMvIgogICAgfSAgZWxzZSB7CiAgICAgIHBhc3RlMCgiaHR0cHM6Ly96ZW5vZG8ub3JnL3JlY29yZHMvIiwgemVuX2lkLCAiL2ZpbGVzLyIpCiAgICB9CiAgICAKICAgICMjIyBSZWNvbnN0cnVjdCB0aGUgaW5wdXQgcGF0aHMKICAgIHJlbW90ZV9wYXRoIDwtIHBhc3RlMChyZW1vdGVfZm9sZGVyLCAiLyIsIHRvZ2V0X2ZpbGVzKQogICAgCiAgICAjIyMgUmVjb25zdHJ1Y3QgdGhlIG91dHB1dCBwYXRocwogICAgbG9jYWxfcGF0aCA8LSBwYXN0ZTAobG9jYWxfZm9sZGVyLCAiLyIsIHRvZ2V0X2ZpbGVzKQogICAgCiAgICAjIyBSZXRyaWV2ZSBmaWxlcyAoaWYgdGhleSBkb24ndCBleGlzdCksIGluIGxvb3AKICAgIGZvciAodGcgaW4gc2VxX2Fsb25nKHRvZ2V0X2ZpbGVzKSkgewogICAgICAjIyBJZiB0aGUgZmlsZSBkb2VzIG5vdCBsb2NhbGx5IGV4aXN0CiAgICAgIGlmICghZmlsZS5leGlzdHMobG9jYWxfcGF0aFt0Z10pIHwgZm9yY2UpIHsKICAgICAgICAjIyBSZXRyaWV2ZSBkYXRhCiAgICAgICAgaWYoYmFja3VwKSB7CiAgICAgICAgICBmaWxlLmNvcHkoZnJvbSA9IHJlbW90ZV9wYXRoW3RnXSwKICAgICAgICAgICAgICAgICAgICB0byA9IGxvY2FsX3BhdGhbdGddKQogICAgICAgIH0gZWxzZSB7CiAgICAgICAgICBkb3dubG9hZC5maWxlKHVybCA9IHJlbW90ZV9wYXRoW3RnXSwgCiAgICAgICAgICAgICAgICAgICAgICAgIGRlc3RmaWxlID0gbG9jYWxfcGF0aFt0Z10pCiAgICAgICAgfQogICAgICAgICMjIENoZWNrIGlmIGRvd25sb2FkZWQgZmlsZXMgZXhpc3QgbG9jYWxseQogICAgICAgIGlmKGZpbGUuZXhpc3RzKGxvY2FsX3BhdGhbdGddKSkgbWVzc2FnZSgiXHRPSyIpCiAgICAgIH0gZWxzZSBtZXNzYWdlKHBhc3RlMCh0b2dldF9maWxlc1t0Z10sICIgYWxyZWFkeSBkb3dubG9hZGVkICEiKSkKICAgIH0KICAgIGBgYAoKPGJyPgoKIyMgTG9hZCBnZW5lIGxpc3RzCgpgYGB7ciBjY19sb2FkfQojIyBUaGUgY2VsbCBjeWNsZSBnZW5lIGxpc3RzIGZpbGUKY2NfZmlsZSA8LSBwYXN0ZTAoZ2xpc3RfZGlyLCAKICAgICAgICAgICAgICAgICAgIi8iLCAKICAgICAgICAgICAgICAgICAgY2NfZmlsZSkKCiMjIExvYWQgdGhlIGNlbGwgY3ljbGUgcmVmZXJlbmNlIGdlbmVzIGxpc3RzCmNjX2dlbmVzIDwtIHJlYWRSRFMoZmlsZSA9IGNjX2ZpbGUpCgojIyBIYXZlIGEgbG9vayBvbiBpdHMgY29udGVudApzdHIoY2NfZ2VuZXMpCmBgYAoKPGJyPgoKLSAgICoqUXVlc3Rpb24qKgogICAgCiAgICBgYGB7ciBxX2NjZ2VuZXMsIGNsYXNzLnNvdXJjZT0icXVlc3Rpb24iLCBldmFsID0gRkFMU0V9CiAgICBBcyBleHBsYWluZWQsIHdlIHdpbGwgdXNlIGdlbmUgbGlzdHMgZXh0cmFjdGVkIGZyb20gY29tbXVuaXR5IGtub3dsZWRnZS4gCiAgICBPdXIgZGF0YSBjb250YWluIHZhbHVlcyBmb3IgZ2VuZXMgYWxzbywgc28gd2Ugd2lsbCBjcm9zcyB0aGVtLiAKICAgIElzIHRoZXJlIHNvbWV0aGluZyB3ZSBzaG91bGQgY2hlY2sgPwogICAgYGBgCiAgICAKICAgIDxicj4KICAgIAogICAgYGBge3IgcV9jY19jaGVjaywgY2xhc3Muc291cmNlID0gYygiZm9sZC1oaWRlIiwgImFuc3dlciIpLCBldmFsID0gRkFMU0V9CiAgICAjIyAuIFdlIG1heSBjaGVjayBpZiB0aGUgZ2VuZXMgaW4gb3VyIGdlbmUgbGlzdHMgYXJlIGVmZmVjdGl2ZWx5IAogICAgIyMgICBwcmVzZW50IGluIG91ciBTZXVyYXQgb2JqZWN0ICEKICAgICMjCiAgICAjIyAuIFRoaXMgaXMgZXhwZWN0ZWQsIGFzIHdlIHJlbW92ZWQgZmVhdHVyZXMgd2l0aCBhbG1vc3Qgbm8gZXhwcmVzc2lvbgogICAgYGBgCiAgICAKICAgIDxicj4KCioqQmV5b25kKiogOgoKMS4gIFdyaXRlIGEgY29kZSB0aGF0IHBlcmZvcm1zIHRoaXMgY2hlY2sKCjIuICBBZGQgYSBjb2RlIHRvIGFkanVzdCB0aGUgY29udGVudCBvZiB0aGUgZ2VuZSBsaXN0cyBhY2NvcmRpbmdseSAocmVtb3ZlIGdlbmVzIGZyb20gdGhlIGdlbmUgbGlzdHMgdGhhdCBhcmUgbm90IHByZXNlbnQgaW4gb3VyIGRhdGFzZXQpCgozLiAgKihOT1RFIDogdGhpcyBpcyBhY3R1YWxseSBub3QgbmVlZGVkIGFzIGFscmVhZHkgY2hlY2tlZCBhbmQKICAgIGNvcnJlY3RlZCBieSB0aGUgY2VsbCBjeWxlIGVzdGltYXRpb24gbWV0aG9kIHdlIHdpbGwgdXNlKSoKCjxicj4KCmBgYHtyIGJfY2NfYW5zd2VyLCBjbGFzcy5zb3VyY2UgPSBjKCJmb2xkLWhpZGUiLCAiYmV5b25kIiksIGNsYXNzLm91dHB1dD0iYmV5b25kbyIsIGV2YWwgPSBGQUxTRX0KIyMgQ2hlY2sgaWYgb3VyIGRhdGEgZ2VuZXMgY292ZXIgdGhlIGdlbmUgbGlzdHMKbGFwcGx5KGNjX2dlbmVzLCBmdW5jdGlvbih4KSB4ICVpbiUgcm93bmFtZXMoc29iaikpCgojIyBSZW1vdmUgZ2VuZXMgbm90IGluIHNvYmoKY2NfZ2VuZXMgPC0gbGFwcGx5KGNjX2dlbmVzLCBmdW5jdGlvbihnbCkgeyBnbFtnbCAlaW4lIHJvd25hbWVzKHNvYmopXSB9KQoKIyMgQ2hlY2sgdGhlIG1vZGlmaWNhdGlvbgpzdHIoY2NfZ2VuZXMpCgojIyBDaGVjayB0aGF0IGFsbCBnZW5lcyBhcmUgYXZhaWxhYmxlIGluIHNvYmoKYWxsKHVuaXF1ZSh1bmxpc3QoY2NfZ2VuZXMpKSAlaW4lIHJvd25hbWVzKHNvYmopKQpgYGAKCjxicj48YnI+CgojIyBFc3RpbWF0aW9uCgotICAgTGV0J3MgcGVyZm9ybSB0aGlzIGVzdGltYXRpb24uIEJ1dCBob3cgPwoKICAgIGBgYHtyIGhfQ2VsbEN5Y2xlU2NvcmluZywgY2xhc3Muc291cmNlID0gIm5vdHJ1biIsIGV2YWwgPSBGQUxTRX0KICAgICMjIFJlYWRpbmcgdGhlIGZ1bmN0aW9uIGhlbHAgcGFnZQogICAgP1NldXJhdDo6Q2VsbEN5Y2xlU2NvcmluZwogICAgYGBgCiAgICAKICAgIDxicj4KCi0gICBXZSB3aWxsIGFjdHVhbGx5IHVzZSBhIGhlbHBlciBmdW5jdGlvbiB0byBlYXNlIHVwIHRoZSBwcm9jZXNzIDoKCiAgICBgYGB7ciBDQ19TZXVyYXQsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gRkFMU0V9CiAgICA/U0MuaGVscGVyOjpDQ19TZXVyYXQKICAgIGBgYAoKPGJyPjxicj48YnI+CgotICAgUnVuIHRoZSBjZWxsLWN5Y2xlIGVzdGltYXRpb24KCiAgICBgYGB7ciBjY19ydW59CiAgICAjIyBQZXJmb3JtIHRoZSBlc3RpbWF0aW9uCiAgICAjIyBUaGUgUk5HIHNlZWQgaXMgbmVlZGVkIGhlcmUgIQogICAgc29iaiA8LSBTQy5oZWxwZXI6OkNDX1NldXJhdCgKICAgICAgc29iaiA9IHNvYmosIAogICAgICBhc3NheSA9ICJSTkEiLAogICAgICBzZXVyYXRfY2NfZ2VuZXMgPSBjY19nZW5lcywgCiAgICAgIFNtRzJNID0gVFJVRSwgCiAgICAgIG5iaW4gPSAyMCwgCiAgICAgIG15X3NlZWQgPSBteV9zZWVkKQogICAgYGBgCgo8YnI+CgotICAgRGVzY3JpcHRpb24gb2YgdGhlIG9iamVjdCB0byBzZWUgdGhlIGRhdGEgYWRkZWQKCiAgICBgYGB7ciBkZXNjX2NjLCBjbGFzcy5zb3VyY2U9Im5vdHJ1biIsIGNsYXNzLm91dHB1dD0ibm90cnVubyJ9CiAgICBTQy5oZWxwZXI6OlNldXJhdE9iamVjdF9kZXNjcmlwdG9yKAogICAgICBzb2JqID0gc29iaiwKICAgICAgZGVzY3JpYmUgPSAiY29sZGF0YSIpCiAgICBgYGAKCjxicj4KCiMjIFZpc3VhbGl6YXRpb24gCgpBcyB1c3VhbCwgd2UgY2FuIHZpc3VhbGl6ZSB0aGUgcmVzdWx0cyBhcyB2aW9saW5zIDoKCmBgYHtyIHZsbmNjLCBmaWcud2lkdGggPSAxMiwgY2xhc3Muc291cmNlPSJub3RydW4iLCBjbGFzcy5vdXRwdXQ9Im5vdHJ1bm8ifQpTZXVyYXQ6OlZsblBsb3Qob2JqZWN0ID0gc29iaiwKICAgICAgICAgICAgICAgIGZlYXR1cmVzID0gYygiQ0NfU2V1cmF0X1MuU2NvcmUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDQ19TZXVyYXRfRzJNLlNjb3JlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ0NfU2V1cmF0X1NtRzJNLlNjb3JlIikpCmBgYAoKPGJyPgoKQnV0IGl0J3Mgbm90IHRoYXQgZWFzeSB0byBpbnRlcnByZXQuLi4gCgpMZXQncyBwbG90IGl0IGluIHRoZSBjZWxsIHNwYWNlCgojIyB7LnVubnVtYmVyZWQgLnRhYnNldCAudGFic2V0LWZhZGUgLnRhYnNldC1waWxsc30KCiMjIyAiUyBtaW51cyBHMk0iIChgU21HMk1gKSBzY29yZSA6CgpgYGB7ciB2aXpjY3N9CiMjIFVzaW5nIHRoZSAnZmVhdHVyZXMnIHBhcmFtZXRlciB0byBwbG90IHRoZSBTbUcyTSBzY29yZSAoY29udGludW91cyBkYXRhKQojIyBIZXJlLCB3ZSB3aWxsIGtlZXAgdGhlIG1vZGlmaWVkIFNldXJhdCBvYmplY3QgdG8gc3BlZWQgdXAgZnVydGhlciBwbG90cwpWSVogPC0gU0MuaGVscGVyOjpRbkRfdml6KHNvYmogPSBzb2JqLCAKICAgICAgICAgICAgICAgICAgICAgICAgICBmZWF0dXJlcyA9ICJDQ19TZXVyYXRfU21HMk0uU2NvcmUiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm5fb2JqZWN0ID0gVFJVRSkKYGBgCgo8YnI+CgojIyMgRXN0aW1hdGVkIGNlbGwgcGhhc2UgOgoKYGBge3Igdml6Y2NwfQojIyBVc2luZyB0aGUgJ2dyb3VwX2J5JyBwYXJhbWV0ZXIgdG8gcGxvdCB0aGUgZXN0aW1hdGVkIHBoYXNlcyAoY2F0ZWdvcmljYWwgZGF0YSkKIyMgSGVyZSwgd2UgcmVjeWNsZSBrZWVwIHRoZSBWSVogU2V1cmF0IG9iamVjdCB0aGF0IGNvbnRhaW5zIGV2ZXJ5dGhpbmcgdG8gcGVyZm9ybSB0aGUgcGxvdCB3aXRob3V0IGNvbXB1dGluZyBpdCBhZ2FpbgpTQy5oZWxwZXI6OlFuRF92aXooc29iaiA9IFZJWiwgCiAgICAgICAgICAgICAgICAgICBzbG90ID0gTlVMTCwgCiAgICAgICAgICAgICAgICAgICBkaW1yZWQgPSAidW1hcCIsIAogICAgICAgICAgICAgICAgICAgZ3JvdXBfYnkgPSAiQ0NfU2V1cmF0X1BoYXNlIikKCiMjIFZJWiBpcyBub3QgbmVlZGVkIGFueW1vcmUKcm0oVklaKQpgYGAKCjxicj4KCiMjIHsudW5udW1iZXJlZH0KCioqUXVlc3Rpb25zKioKCmBgYHtyIHFfY2VsbDEsIGNsYXNzLnNvdXJjZT0icXVlc3Rpb24iLCBldmFsID0gRkFMU0V9CkRvZXMgdGhlIHN0cnVjdHVyZSBvZiB0aGUgY2VsbHMgaW4gdGhpcyByZXByZXNlbnRhdGlvbiBzZWVtIHRvIGhhdmUgYSBsaW5rIHdpdGggdGhlc2UgY2VsbCBjeWNsZSBwaGFzZXMvc2NvcmVzID8KYGBgCgo8YnI+CgpgYGB7ciBhX2NlbGwxLCBjbGFzcy5zb3VyY2UgPSBjKCJmb2xkLWhpZGUiLCAiYW5zd2VyIiksIGV2YWwgPSBGQUxTRX0KIyMgSXQncyBhbiBhYnNvbHV0ZSB5ZXMgIQpgYGAKCjxicj4KCmBgYHtyIHFfY2VsbDIsIGNsYXNzLnNvdXJjZT0icXVlc3Rpb24iLCBldmFsID0gRkFMU0V9CkRvIHlvdSB0aGluayB0aGlzIGlzIHRoZSByZXN1bHQgb2YgYW4gYXJ0aWZhY3QsIG9yCmJpb2xvZ3ktcmVsYXRlZCA/CmBgYAoKPGJyPgoKYGBge3IgYV9jZWxsMiwgY2xhc3Muc291cmNlID0gYygiZm9sZC1oaWRlIiwgImFuc3dlciIpLCBldmFsID0gRkFMU0V9CiMjCiMjICAgIMKvXF8o44OEKV8vwq8KIyMKYGBgCgo8YnI+CgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBTYXZlIHRoZSBTZXVyYXQgb2JqZWN0CgpXZSB3aWxsIHNhdmUgb3VyIFNldXJhdCBvYmplY3QgdGhhdCBub3cgY29udGFpbnMgdGhlIGNlbGwgY3ljbGUKc3RhdGVzL3Njb3JlcyA6CgpgYGB7ciBzYXZlcmRzMSwgZm9sZC5vdXRwdXQgPSBGQUxTRX0KIyMgU2F2ZSBvdXIgU2V1cmF0IG9iamVjdCAocmljaCBuYW1pbmcpCm91dF9uYW1lIDwtIHBhc3RlMCgKICAgICAgICAgIG91dHB1dF9kaXIsICIvIiwgcGFzdGUoCiAgICAgICAgICAgIGMoIjA0IiwgU2V1cmF0OjpQcm9qZWN0KHNvYmopLCAiUzUiLCAKICAgICAgICAgICAgICAiQ0MiLCBwYXN0ZSgKICAgICAgICAgICAgICAgIGRpbShzb2JqKSwgCiAgICAgICAgICAgICAgICBjb2xsYXBzZSA9ICcuJwogICAgICAgICAgICAgICkKICAgICAgICAgICAgKSwgY29sbGFwc2UgPSAiXyIpLAogICAgICAgICAgICAiLlJEUyIpCgojIyBDaGVjawpwcmludChvdXRfbmFtZSkKCiMjIFdyaXRlIG9uIGRpc2sKc2F2ZVJEUyhvYmplY3QgPSBzb2JqLCAKICAgICAgICBmaWxlID0gb3V0X25hbWUpCmBgYAoKPGJyPgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCjxicj4KCiMgQ2VsbCBkb3VibGV0cwoKIyMgey50YWJzZXQgLnRhYnNldC1mYWRlIC50YWJzZXQtcGlsbHN9CgpXZSB3aWxsIHVzZSAqKnR3byBkaWZmZXJlbnQgbWV0aG9kcyoqIHRvIGRldGVjdCBhbmQgcmVtb3ZlIGNlbGwgZG91YmxldHMgOgoKLSAgIFtgc2Nkc2BdKGh0dHBzOi8vd3d3LmJpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvcmVsZWFzZS9iaW9jL2h0bWwvc2Nkcy5odG1sKSAoaW4gaXRzICJoeWJyaWQiIG1vZGUpe3RhcmdldD0iX2JsYW5rIn0gOiBtb3JlIGVmZmljaWVudCBhdCBkZXRlY3RpbmcgX2hvbW90eXBpY18gZG91YmxldHMKCi0gICBbc2NEYmxGaW5kZXJdKGh0dHBzOi8vd3d3LmJpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvcmVsZWFzZS9iaW9jL2h0bWwvc2NEYmxGaW5kZXIuaHRtbCl7dGFyZ2V0PSJfYmxhbmsifSA6IG1vcmUgZWZmaWNpZW50IGF0IGRldGVjdGluZyBfaGV0ZXJvdHlwaWNfIGRvdWJsZXRzCgpOb25lIG9mIHRoZSBtZXRob2RzIGFjY2VwdHMgYSBgU2V1cmF0T2JqZWN0YCBhcyBpbnB1dCwgYnV0IGEgW2BTaW5nbGVDZWxsRXhwZXJpbWVudGBdKGh0dHBzOi8vd3d3LmJpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvcmVsZWFzZS9iaW9jL2h0bWwvU2luZ2xlQ2VsbEV4cGVyaW1lbnQuaHRtbCl7dGFyZ2V0PSJfYmxhbmsifSBvYmplY3QuCgpIb3BlZnVsbHkgOgoKLSAgIFNldXJhdCBoYXMgYSBmdW5jdGlvbiB0byBwZXJmb3JtIHRoZSBjb252ZXJzaW9uCi0gICBUaGUgb3V0cHV0IHJlc3VsdHMgY2FuIGJlIGludGVncmF0ZWQgaW50byBvdXIgU2V1cmF0IG9iamVjdCB3aXRoIGVhc2UKCiMjIGBzY2RzYAoKYGBge3Igc2Nkc30KIyMgRml4IHNlZWQKc2V0LnNlZWQobXlfc2VlZCkKCiMjIFJ1biBzY2RzCnNvYmokZG91YmxldF9zY2RzLmh5YnJpZCA8LSB1bm5hbWUoCiAgc2Nkczo6Y3hkc19iY2RzX2h5YnJpZCgKICAgIFNldXJhdDo6YXMuU2luZ2xlQ2VsbEV4cGVyaW1lbnQoCiAgICAgIHNvYmosIGFzc2F5ID0gIlJOQSIpKSRoeWJyaWRfc2NvcmUgPiAxKQoKIyMgQ29udGluZ2VuY2llcwp0YWJsZShzb2JqJGRvdWJsZXRfc2Nkcy5oeWJyaWQpCmBgYAoKCiMjIHNjRGJsRmluZGVyCgpgYGB7ciBzY2RibH0KIyMgRml4IHNlZWQKc2V0LnNlZWQobXlfc2VlZCkKCiMjIFJ1biBzY0RibEZpbmRlciAod2hpY2ggbmVlZHMgYW5vdGhlciBvYmplY3QgdHlwZSkKc29iaiRkb3VibGV0X3NjRGJsRmluZGVyIDwtIHNjRGJsRmluZGVyOjpzY0RibEZpbmRlcigKICBzY2UgPSBTZXVyYXQ6OmFzLlNpbmdsZUNlbGxFeHBlcmltZW50KAogICAgeCA9IHNvYmosIAogICAgYXNzYXkgPSAiUk5BIiksCiAgcmV0dXJuVHlwZSA9ICJ0YWJsZSIpJGNsYXNzID09ICJkb3VibGV0IgoKIyMgQ29udGluZ2VuY2llcwp0YWJsZShzb2JqJGRvdWJsZXRfc2NEYmxGaW5kZXIpCmBgYAoKIyAgey51bm51bWJlcmVkfQoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCjxicj4KCiMjIE1lcmdlIHJlc3VsdHMKCldlIG1lcmdlIHJlc3VsdHMgb2YgdGhlIHR3byBtZXRob2RzCgpgYGB7ciBkYmxtZXJnZX0KIyMgTG9naWNhbCB1bmlvbiBvZiBib3RoIG1ldGhvZHMKc29iaiRkb3VibGV0X3VuaW9uIDwtIHNvYmokZG91YmxldF9zY2RzLmh5YnJpZCB8IHNvYmokZG91YmxldF9zY0RibEZpbmRlcgoKIyMgUXVhbnRpZnkgZG91YmxldHMKdGFibGUoc29iaiRkb3VibGV0X3VuaW9uKQpgYGAKCjxicj4KCldlIGNhbiBhc3Nlc3MgKip0b29sLXNwZWNpZmljKiogYW5kICoqY29tbW9uKiogZG91YmxldHMKCmBgYHtyIGRibF90eXBlcywgY2xhc3Muc291cmNlPSJub3RydW4iLCBjbGFzcy5vdXRwdXQ9Im5vdHJ1bm8ifQojIyMgU2luZ2xldHMgYnkgZGVmYXVsdApzb2JqJGRvdWJsZXRfdml6IDwtICJzaW5nbGV0IgoKIyMjIFVuaW9uCnNvYmokZG91YmxldF92aXpbc29iaiRkb3VibGV0X3VuaW9uXSA8LSAiYm90aCIKCiMjIyBzY2RzLXNwZWNpZmljCnNvYmokZG91YmxldF92aXpbc29iaiRkb3VibGV0X3NjZHMuaHlicmlkICYgIXNvYmokZG91YmxldF9zY0RibEZpbmRlcl0gPC0gInNjZHMiCgojIyMgc2NEYmxGaW5kZXItc3BlY2lmaWMKc29iaiRkb3VibGV0X3Zpeltzb2JqJGRvdWJsZXRfc2NEYmxGaW5kZXIgJiAhc29iaiRkb3VibGV0X3NjZHMuaHlicmlkXSA8LSAic2NEYmxGaW5kZXIiCgojIyBDb252ZXJ0IHRvIGZhY3Rvcgpzb2JqJGRvdWJsZXRfdml6IDwtIGFzLmZhY3Rvcihzb2JqJGRvdWJsZXRfdml6KQoKIyMgQ29udGluZ2VuY2llcwp0YWJsZShzb2JqJGRvdWJsZXRfdml6KQpgYGAKCjxicj4KCioqQmV5b25kKiogOiBDcmVhdGUgYW4gdXBzZXQtcGxvdCBmb3IgdGhlIGRvdWJsZXQgc3RhdHVzIGFjY29yZGluZyB0byB0aGUgdHdvIG1ldGhvZHMgdXNlZAoKYGBge3IgYl91cHNldCwgY2xhc3Muc291cmNlPWMoImZvbGQtaGlkZSIsICJiZXlvbmQiKSwgY2xhc3Mub3V0cHV0PSJiZXlvbmRvIn0KIyMgQnVpbGQgYSBsaXN0IG9mIGNlbGxzIHRhZ2dlZCBieSBvbmUgdG9vbCwgdGhlIG90aGVyLCBvciBib3RoCmRibF9saXN0IDwtbGlzdCgKICAic2NkcyIgPSBjb2xuYW1lcyhzb2JqKVtzb2JqJGRvdWJsZXRfc2Nkcy5oeWJyaWQgJiAhc29iaiRkb3VibGV0X3NjRGJsRmluZGVyXSwKICAic2NEYmxGaW5kZXIiID0gY29sbmFtZXMoc29iailbIXNvYmokZG91YmxldF9zY2RzLmh5YnJpZCAmIHNvYmokZG91YmxldF9zY0RibEZpbmRlcl0sCiAgImJvdGgiID0gY29sbmFtZXMoc29iailbc29iaiRkb3VibGV0X3NjZHMuaHlicmlkICYgc29iaiRkb3VibGV0X3NjRGJsRmluZGVyXSkKCiMjIERyYXcgdGhlIHVwc2V0IHBsb3QKVXBTZXRSOjp1cHNldChkYXRhID0gVXBTZXRSOjpmcm9tTGlzdChkYmxfbGlzdCksIAogICAgICAgICAgICAgIG5pbnRlcnNlY3RzID0gTkEsIAogICAgICAgICAgICAgIHNldHMgPSByZXYobmFtZXMoZGJsX2xpc3QpKSwKICAgICAgICAgICAgICBrZWVwLm9yZGVyID0gVFJVRSwKICAgICAgICAgICAgICBvcmRlci5ieSA9ICJmcmVxIikKYGBgCgo8YnI+CgpOb3cgd2UgY2FuIHJlbW92ZSBiYXJjb2RlcyBpZGVudGlmaWVkIGFzIGNlbGwgZG91YmxldHMsIGFuZCB2aXN1YWxpemUgdGhlIGNlbGwgc3BhY2UgYmVmb3JlIGFuZCBhZnRlci4KCiMjIERvdWJsZXRzIGZpbHRlcmluZyB7LnRhYnNldCAudGFic2V0LWZhZGUgLnRhYnNldC1waWxsc30KCiMjIyBCRUZPUkUKCmBgYHtyIGRibHZpejEsIGNsYXNzLnNvdXJjZT0ibm90cnVuIiwgY2xhc3Mub3V0cHV0PSJub3RydW5vIn0KIyMjIERvdWJsZXRzIHZpeiAoYmVmb3JlIHJlbW92YWwpCnVtYXBfZGJsX3VuZmlsdCA8LSBTQy5oZWxwZXI6OlFuRF92aXooCiAgc29iaiA9IHNvYmosIAogIGdyb3VwX2J5ID0gImRvdWJsZXRfdml6IiwKICByZXR1cm5fcGxvdCA9IFRSVUUpCmBgYAoKIyMjIFJlbW92ZSBkb3VibGV0cwoKYGBge3IgZGJscm0sIGZvbGQub3V0cHV0ID0gRkFMU0V9CiMjIERpbWVuc2lvbnMgYmVmb3JlIHJlbW92YWwKZGltKHNvYmopCgojIyBQZXJmb3JtIHRoZSByZW1vdmFsCnNvYmogPC0gc29ialssIXNvYmokZG91YmxldF91bmlvbl0KCiMjIERpbWVuc2lvbnMgYWZ0ZXIKZGltKHNvYmopCmBgYAoKIyMjIEFGVEVSCgpgYGB7ciBkYmx2aXoyfQojIyMgRG91YmxldHMgdml6IChhZnRlciByZW1vdmFsKQp1bWFwX2RibF9maWx0IDwtIFNDLmhlbHBlcjo6UW5EX3ZpeigKICBzb2JqID0gc29iaiwgCiAgZ3JvdXBfYnkgPSAiZG91YmxldF92aXoiLAogIHJldHVybl9wbG90ID0gVFJVRSkKYGBgCgojIyAgey51bm51bWJlcmVkfQoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCjxicj4KCk1lcmdlIHBsb3RzIGZvciBlYXNlIG9mIHVzZSA6CgpgYGB7ciBkYmxfdW1hcHMsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gNiwgY2xhc3Muc291cmNlPWMoImZvbGQtaGlkZSIsICJub3RydW4iKSwgY2xhc3Mub3V0cHV0PSJub3RydW5vIn0KIyMgVXNpbmcgdGhlIHBhdGNod29yayBwYWNrYWdlIHRvIG1lcmdlIHBsb3RzIChhbmQgZ2dwbG90MiB0byBhZGQgdGl0bGVzKQpwYXRjaHdvcms6OndyYXBfcGxvdHMoCiAgbGlzdCgKICAgIHVtYXBfZGJsX3VuZmlsdCAmIGdncGxvdDI6OmdndGl0bGUobGFiZWwgPSAiQ2VsbCBkb3VibGV0cyAodW5maWx0ZXJlZCkiKSwKICAgIHVtYXBfZGJsX2ZpbHQgJiBnZ3Bsb3QyOjpnZ3RpdGxlKGxhYmVsID0gIkNlbGwgZG91YmxldHMgKGZpbHRlcmVkKSIpKSwgCiAgbnJvdyA9IDEpCmBgYAoKPGJyPgoKKipRdWVzdGlvbioqCgpgYGB7ciBxX2RibGNvbXAsIGNsYXNzLnNvdXJjZT0icXVlc3Rpb24iLCBldmFsPUZBTFNFfQpXaGF0IGRvIHlvdSBvYnNlcnZlIHdoZW4gY29tcGFyaW5nIGJlZm9yZSBhbmQgYWZ0ZXIgdGhlIGRvdWJsZXRzIGZpbHRlcmluZyA/CmBgYAoKPGJyPgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCgojIFNhdmUgdGhlIFNldXJhdCBvYmplY3QKCldlIHdpbGwgc2F2ZSBvdXIgU2V1cmF0IG9iamVjdCB0aGF0IGlzIG5vdyBmaWx0ZXJlZCBmb3IgZG91YmxldHMgOgoKYGBge3Igc2F2ZXJkczN9CiMjIFNhdmUgb3VyIFNldXJhdCBvYmplY3QgKHJpY2ggbmFtaW5nKQpvdXRfbmFtZSA8LSBwYXN0ZTAoCiAgICAgICAgICBvdXRwdXRfZGlyLCAiLyIsIHBhc3RlKAogICAgICAgICAgICBjKCIwNSIsIFNldXJhdDo6UHJvamVjdChzb2JqKSwgIlM1IiwgCiAgICAgICAgICAgICAgIkRvdWJsZXRzLkZpbHRlcmVkIiwgcGFzdGUoCiAgICAgICAgICAgICAgICBkaW0oc29iaiksIAogICAgICAgICAgICAgICAgY29sbGFwc2UgPSAnLicKICAgICAgICAgICAgICApCiAgICAgICAgICAgICksIGNvbGxhcHNlID0gIl8iKSwKICAgICAgICAgICAgIi5SRFMiKQoKIyMgQ2hlY2sKcHJpbnQob3V0X25hbWUpCgojIyBXcml0ZSBvbiBkaXNrCnNhdmVSRFMob2JqZWN0ID0gc29iaiwgCiAgICAgICAgZmlsZSA9IG91dF9uYW1lKQpgYGAKCjxicj4KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgo8YnI+PGJyPjxicj4KCiMgUnNlc3Npb24KCmBgYHtyIHJzZXNzaW9uLCBjbGFzcy5zb3VyY2U9Im5vdHJ1biIsIGNsYXNzLm91dHB1dD0ibm90cnVubyJ9CnV0aWxzOjpzZXNzaW9uSW5mbygpCmBgYAo=