PREAMBLE
Purpose of this
session
This file describes the different steps to perform
fifth part of data processing for the single cell
RNAseq data analysis training course for the EBAII n1
2024, covering these steps :
Dimension reduction of the expression
data
Visualization of cells expression in a 2-D
space
Unsupervised clustering of cells
Description of the defined clusters
Warm-up
- We set common parameters we will use throughout
this session :
## Seed for the RNG
my_seed <- 1337L
## Dimensions to keep from dimension reduction
n_dim <- 20
## Resolution for Louvain clustering
l_res <- .8
Prepare the data
structure
We will do the same as for former steps, just changing the session
name :
Main directory
## Setting the project name
project_name <- "ebaii_sc_teachers" # Do not copy-paste this ! It's MY project !!
## 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.2) directory
session_dir <- paste0(TD_dir, "/05_Proc.2")
dir.create(path = session_dir, recursive = TRUE)
## Print the session directory on-screen
print(session_dir)
[1] "/shared/projects/ebaii_sc_teachers/SC_TD/05_Proc.2"
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/05_Proc.2/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 <- "08_TD3A_S5_Scaled.2k_Reg.PCrb_12508.4035.RDS"
## The latest Seurat object saved as RDS (full path)
sobj_path <- paste0(TD_dir,
"/04_Proc.1/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)
Dimension
reduction
This step originates from the observation that we do not want nor
need to characterize each of our thousends of
cells, but groups of them (clusters ? cell
types ? other ?). Thus, we do no need all data, and even may benefit
from such a reduction :
- Reduce the data complexity
- For interpretation
- For computations
- Increase the quality of information contained in the data
- Enriching “good biological
signals”
- Discarding noise / cell-specific signals
There is a
multitude of methods for dimension reduction
Principal Component
Analysis (PCA)
Here, we will use the grand-mother of all : the PCA (Principal
Component Analysis)
Questions : ⭍⭍ Lightning quizz ⭍⭍ :
How many principal components (PC) will be generated by default ?
## . The answer is 50 (npcs parameter)
##
## . We will use this default value.
##
## . Warning : in some (rare) contexts, this
## may not be enough !
Which data type (ie, which Seurat object layer) will be used
to generate the components ?
## . Data from the scale.data layer will be used
##
## . This is unfortunately not explicit
## from the Seurat::RunPCA help page !
Perform PCA on our data
## Note : a seed is used here !
sobj <- Seurat::RunPCA(
object = sobj,
assay = 'RNA',
seed.use = my_seed,
verbose = FALSE)
Description :
SC.helper::SeuratObject_descriptor(sobj = sobj, describe = 'dimred')
Show output
OBJECT VERSION : 5.0.2
PROJECT : [TD3A]
[DIMREDS]
DIMRED 1 : [pca] Dims:[4035 x 50]
Visualization of the very first two components, with
cells coloring according to the estimated cell cycle
phase :
## Scatter plot along dimensions
Seurat::DimPlot(
object = sobj,
## First two components
dims = c(1,2),
## Color dots per cell phase groups
group.by = 'CC_Seurat_Phase',
## Data to use
reduction = 'pca')
Show plot
Questions
Give us your interpretation / feelings from this plot !
Should we limit ourselves to using 2 dimensions to interpret our data ?
##
## __ .___/\ .__ __ .__ __ .__ .___ ._.
## / / ______ | )/_____ __ _ _|__|/ |_| |__ _______/ |_ __ ________ |__| __| _/ | |
## / / /_____/ | |/ \ \ \/ \/ / \ __\ | \ / ___/\ __\ | \____ \| |/ __ | | |
## \ \ /_____/ | | Y Y \ \ /| || | | Y \ \___ \ | | | | / |_> > / /_/ | \|
## \_\ |___|__|_| / \/\_/ |__||__| |___| / /____ / |__| |____/| __/|__\____ | __
## \/ \/ \/ |__| \/ \/
##
Visualization
This final processing step need to finally observe
our data requires a novel dimension reduction method with a very high
challenge to overcome : reduce a space of dozens of dimensions to
just a few !
We will use the UMAP method.
Bonus : 3D UMAP
(DEMO)
While by default Seurat::RunUMAP will produce 2-dimension reductions,
the method can generate further components.
Despite our limited brain, this is sometimes interesting and useful
to attempt a reduction to 3 dimensions instead of 2. This can be very
effective when looking for trajectories.
We can generate a UMAP with 3 components from 20` PCs :
## UMAP from 25 PCs, 3 components requested
sobj <- Seurat::RunUMAP(
object = sobj, assay = 'RNA',
graph.name = 'RNA_snn',
reduction = 'pca',
reduction.name = 'umap3d',
dims = 1:n_dim,
seed.use = my_seed,
n.components = 3)
## DimPlot of the first 2 UMAP components
Seurat::DimPlot(
object = sobj,
dims = c(1,2),
reduction = 'umap3d')
Show plot
Question :
Isn't there something striking ?
## . The plot is not the same as when
## using 25 PCs and requesting 2 UMAP
## components instead of 3 here !
## . The 2 components of a 2D UMAP are not
## the same as the two first components
## of a dim>2 UMAP.
Let’s perform a 3D representation of our UMAP
## Structure data to plot in a data.frame
df3d <- as.data.frame(
Seurat::Reductions(object = sobj,
slot = "umap3d")@cell.embeddings
)
## 3D plot
plotly::plot_ly(
data = df3d,
x = ~umap3d_1,
y = ~umap3d_2,
z = ~umap3d_3,
type = 'scatter3d',
marker = list(size = 2, width=2))
Save the Seurat
object
We will save our Seurat object that now contains PCA and UMAP
reductions :
## Save our Seurat object (rich naming)
out_name <- paste0(
output_dir, "/", paste(
c("09", Seurat::Project(sobj), "S5",
"DimRed.PCA", paste(
dim(sobj),
collapse = '.'
)
), collapse = "_"),
".RDS")
## Check
print(out_name)
[1] "/shared/projects/ebaii_sc_teachers/SC_TD/05_Proc.2/RESULTS/09_TD3A_S5_DimRed.PCA_12508.4035.RDS"
## Write on disk
saveRDS(object = sobj,
file = out_name)
Clustering
We can now attempt to determine how cells are
organized in an unsupervised manner in this
space
We will use the graph-based clustering method Louvain
Clustering will be performed on the PCA dimension
reduction, not on the UMAP one
Find neighbours
Before running the Louvain method, a first pass method is used to
generate a “K-Nearest Neighbour” graph (see more details here).
## Compute a SNN using the first 20 PCs
sobj <- Seurat::FindNeighbors(
object = sobj,
dims = 1:20,
reduction = "pca")
Louvain
clustering
## Louvain resolutions to test
resol <- c(.3, 0.8, 1.5)
## Clustering
sobj <- Seurat::FindClusters(
object = sobj,
resolution = resol,
verbose = FALSE)
Question
Could you tell us what changed in our object ?
## One can just call it :
sobj
Show output
An object of class Seurat
12508 features across 4035 samples within 1 assay
Active assay: RNA (12508 features, 2000 variable features)
3 layers present: counts, data, scale.data
3 dimensional reductions calculated: pca, umap, umap3d
### Hmmm, nothing new under the sun ...
## One can describe it :
SC.helper::SeuratObject_descriptor(
sobj = sobj,
describe = "coldata")
Show output
OBJECT VERSION : 5.0.2
PROJECT : [TD3A]
[GRAPHS]
RNA_nn
RNA_snn
[BARCODES METADATA]
orig.ident Freq
----------- -----
TD3A 4035
NA 0
nCount_RNA
Min. 1st Qu. Median Mean 3rd Qu. Max.
1000 2033 2397 3497 2998 48866
nFeature_RNA
Min. 1st Qu. Median Mean 3rd Qu. Max.
750 1316 1476 1638 1690 5968
log10_nCount_RNA
Min. 1st Qu. Median Mean 3rd Qu. Max.
3.000 3.309 3.380 3.438 3.477 4.689
nCount_RNA_in_range Freq
-------------------- -----
TRUE 4035
NA 0
nFeature_RNA_in_range Freq
---------------------- -----
TRUE 4035
NA 0
percent_mt
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.001801 0.019608 0.024401 0.025177 0.029933 0.050000
percent_rb
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.02687 0.08010 0.09640 0.11062 0.12154 0.42010
percent_st
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.01546 0.02986 0.03364 0.03423 0.03778 0.05980
percent_mt_in_range Freq
-------------------- -----
TRUE 4035
NA 0
percent_rb_in_range Freq
-------------------- -----
TRUE 4035
NA 0
percent_st_in_range Freq
-------------------- -----
TRUE 4035
NA 0
CC_Seurat_S.Score
Min. 1st Qu. Median Mean 3rd Qu. Max.
-0.233844 -0.105613 -0.058412 -0.022117 -0.001082 1.232905
CC_Seurat_G2M.Score
Min. 1st Qu. Median Mean 3rd Qu. Max.
-0.22728 -0.11205 -0.07244 -0.04314 -0.02550 1.35266
CC_Seurat_Phase Freq
---------------- -----
G1 2718
G2M 435
S 882
NA 0
CC_Seurat_SmG2M.Score
Min. 1st Qu. Median Mean 3rd Qu. Max.
-1.16787 -0.04463 0.01571 0.02102 0.08349 0.96435
doublet_scds.hybrid Freq
-------------------- -----
FALSE 4035
NA 0
doublet_scDblFinder Freq
-------------------- -----
FALSE 4035
NA 0
doublet_union Freq
-------------- -----
FALSE 4035
NA 0
doublet_viz Freq
------------ -----
both 0
scDblFinder 0
scds 0
singlet 4035
NA 0
RNA_snn_res.0.3 Freq
---------------- -----
0 1989
1 873
2 418
3 204
4 171
5 159
6 118
7 103
NA 0
RNA_snn_res.0.8 Freq
---------------- -----
0 931
1 724
2 680
3 480
4 417
5 218
6 203
7 159
8 119
9 104
NA 0
RNA_snn_res.1.5 Freq
---------------- -----
0 455
1 455
2 414
3 372
4 371
5 337
6 301
7 257
8 241
9 210
10 201
11 159
12 119
13 104
14 39
NA 0
seurat_clusters Freq
---------------- -----
0 455
1 455
2 414
3 372
4 371
5 337
6 301
7 257
8 241
9 210
10 201
11 159
12 119
13 104
14 39
NA 0
Visualization &
selection
On UMAPs
Plotting UMAPs harboring the clustering results for our 3 tested
resolutions
## Metadata name of clustering results
resol_names <- paste0("RNA_snn_res.", resol)
Seurat::DimPlot(
object = sobj,
reduction = "umap",
group.by = resol_names,
label = TRUE,
repel = TRUE)
Show plot
Clusters
contingencies and proportions
One can observe how many cells are in each cluster, and what
proportion of all cells these represent
for (x in resol_names) {
## Contingencies
print(table(sobj[[x]]))
## Proportions
print(format(table(sobj[[x]]) / ncol(sobj), digits = 2))
cat('\n')
}
Show output
RNA_snn_res.0.3
0 1 2 3 4 5 6 7
1989 873 418 204 171 159 118 103
RNA_snn_res.0.3
0 1 2 3 4 5 6 7
"0.493" "0.216" "0.104" "0.051" "0.042" "0.039" "0.029" "0.026"
RNA_snn_res.0.8
0 1 2 3 4 5 6 7 8 9
931 724 680 480 417 218 203 159 119 104
RNA_snn_res.0.8
0 1 2 3 4 5 6 7 8 9
"0.231" "0.179" "0.169" "0.119" "0.103" "0.054" "0.050" "0.039" "0.029" "0.026"
RNA_snn_res.1.5
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
455 455 414 372 371 337 301 257 241 210 201 159 119 104 39
RNA_snn_res.1.5
0 1 2 3 4 5 6 7
"0.1128" "0.1128" "0.1026" "0.0922" "0.0919" "0.0835" "0.0746" "0.0637"
8 9 10 11 12 13 14
"0.0597" "0.0520" "0.0498" "0.0394" "0.0295" "0.0258" "0.0097"
Cluster-specific
markers
A practical way to characterize our clustering results is to get back
to a level of knowledge you are confident in : marker genes.
Seurat has a handy function to :
Identify differential expressed genes specific to each and every
provided category of cells (here, clustering results)
Draw a clusterized, annotated heatmap of these genes
## Looping on clustering results
fma_all <- lapply(resol_names, function(r) {
## Find markers for all clusters
Seurat::Idents(object = sobj) <- sobj[[r]][[1]]
fam <- Seurat::FindAllMarkers(
object = sobj,
logfc.threshold = .5,
only.pos = TRUE,
min.pct = .5,
verbose = FALSE,
random.seed = my_seed)
## Select top10 genes when available
fam_rdx <- dplyr::group_by(.data = fam, cluster)
fam_rdx <- dplyr::filter(.data = fam_rdx, avg_log2FC > 1)
fam_rdx <- dplyr::slice_head(.data = fam_rdx, n = 10)
dh <- Seurat::DoHeatmap(object = sobj, features = fam_rdx$gene, combine = TRUE) + ggplot2::ggtitle(label = r)
return(dh)
})
## Plot all heatmaps at once
patchwork::wrap_plots(fma_all) + patchwork::plot_layout(nrow = 1)
Show plot
Questions : Comparing the heatmaps :
Which resolution would you choose, and why ?
Is there a single one and only answer to the former question ?
Selection
For the downstream analyses, we will use the resolution
0.8
Seurat::Idents(object = sobj) <- sobj[[paste0("RNA_snn_res.", l_res)]][[1]]
Save the Seurat
object
We will save our Seurat object that now contains our clustering
results :
## Save our Seurat object (rich naming)
out_name <- paste0(
output_dir, "/", paste(
c("10", Seurat::Project(sobj), "S5",
paste0(
"Clustered.",
l_res),
paste(
dim(sobj),
collapse = '.'
)
), collapse = "_"),
".RDS")
## Check
print(out_name)
[1] "/shared/projects/ebaii_sc_teachers/SC_TD/05_Proc.2/RESULTS/10_TD3A_S5_Clustered.0.8_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
loaded via a namespace (and not attached):
[1] matrixStats_1.4.1 spatstat.sparse_3.1-0
[3] SC.helper_0.0.6 httr_1.4.7
[5] RColorBrewer_1.1-3 doParallel_1.0.17
[7] alabaster.base_1.4.1 tools_4.4.1
[9] sctransform_0.4.1 backports_1.5.0
[11] utf8_1.2.4 R6_2.5.1
[13] HDF5Array_1.32.0 lazyeval_0.2.2
[15] uwot_0.2.2 rhdf5filters_1.16.0
[17] GetoptLong_1.0.5 withr_3.0.1
[19] sp_2.1-4 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] foreign_0.8-86 parallelly_1.38.0
[35] limma_3.60.6 rstudioapi_0.17.0
[37] RSQLite_2.3.7 generics_0.1.3
[39] shape_1.4.6.1 ica_1.0-3
[41] spatstat.random_3.3-2 dplyr_1.1.4
[43] Matrix_1.7-1 fansi_1.0.6
[45] S4Vectors_0.42.1 abind_1.4-8
[47] lifecycle_1.0.4 SoupX_1.6.2
[49] yaml_2.3.10 edgeR_4.2.2
[51] SummarizedExperiment_1.34.0 rhdf5_2.48.0
[53] SparseArray_1.4.8 BiocFileCache_2.12.0
[55] Rtsne_0.17 grid_4.4.1
[57] blob_1.2.4 promises_1.3.0
[59] dqrng_0.4.1 ExperimentHub_2.12.0
[61] crayon_1.5.3 miniUI_0.1.1.1
[63] lattice_0.22-6 beachmat_2.20.0
[65] cowplot_1.1.3 KEGGREST_1.44.0
[67] pillar_1.9.0 knitr_1.48
[69] ComplexHeatmap_2.20.0 metapod_1.12.0
[71] GenomicRanges_1.56.2 rjson_0.2.21
[73] future.apply_1.11.2 codetools_0.2-20
[75] leiden_0.4.3.1 glue_1.8.0
[77] spatstat.univar_3.0-1 data.table_1.16.2
[79] gypsum_1.0.1 vctrs_0.6.5
[81] png_0.1-8 spam_2.11-0
[83] gtable_0.3.5 cachem_1.1.0
[85] xfun_0.48 S4Arrays_1.4.1
[87] mime_0.12 survival_3.7-0
[89] SingleCellExperiment_1.26.0 iterators_1.0.14
[91] statmod_1.5.0 bluster_1.14.0
[93] fitdistrplus_1.2-1 ROCR_1.0-11
[95] nlme_3.1-165 bit64_4.5.2
[97] alabaster.ranges_1.4.1 filelock_1.0.3
[99] RcppAnnoy_0.0.22 GenomeInfoDb_1.40.1
[101] bslib_0.8.0 irlba_2.3.5.1
[103] KernSmooth_2.23-24 rpart_4.1.23
[105] colorspace_2.1-1 BiocGenerics_0.50.0
[107] DBI_1.2.3 Hmisc_5.1-3
[109] celldex_1.14.0 nnet_7.3-19
[111] tidyselect_1.2.1 bit_4.5.0
[113] compiler_4.4.1 curl_5.2.3
[115] httr2_1.0.1 htmlTable_2.4.2
[117] BiocNeighbors_1.22.0 DelayedArray_0.30.1
[119] plotly_4.10.4 checkmate_2.3.1
[121] scales_1.3.0 lmtest_0.9-40
[123] rappdirs_0.3.3 stringr_1.5.1
[125] digest_0.6.37 goftest_1.2-3
[127] spatstat.utils_3.1-0 alabaster.matrix_1.4.1
[129] rmarkdown_2.28 XVector_0.44.0
[131] htmltools_0.5.8.1 pkgconfig_2.0.3
[133] base64enc_0.1-3 SingleR_2.6.0
[135] sparseMatrixStats_1.16.0 MatrixGenerics_1.16.0
[137] highr_0.11 dbplyr_2.5.0
[139] fastmap_1.2.0 rlang_1.1.4
[141] GlobalOptions_0.1.2 htmlwidgets_1.6.4
[143] UCSC.utils_1.0.0 shiny_1.9.1
[145] DelayedMatrixStats_1.26.0 farver_2.1.2
[147] jquerylib_0.1.4 zoo_1.8-12
[149] jsonlite_1.8.9 BiocParallel_1.38.0
[151] BiocSingular_1.20.0 magrittr_2.0.3
[153] Formula_1.2-5 scuttle_1.14.0
[155] GenomeInfoDbData_1.2.12 dotCall64_1.2
[157] patchwork_1.3.0 Rhdf5lib_1.26.0
[159] munsell_0.5.1 Rcpp_1.0.13
[161] reticulate_1.39.0 alabaster.schemas_1.4.0
[163] stringi_1.8.4 zlibbioc_1.50.0
[165] MASS_7.3-61 AnnotationHub_3.12.0
[167] plyr_1.8.9 parallel_4.4.1
[169] listenv_0.9.1 ggrepel_0.9.6
[171] deldir_2.0-4 Biostrings_2.72.1
[173] splines_4.4.1 tensor_1.5
[175] circlize_0.4.16 locfit_1.5-9.9
[177] igraph_2.1.1 spatstat.geom_3.3-3
[179] RcppHNSW_0.6.0 reshape2_1.4.4
[181] stats4_4.4.1 ScaledMatrix_1.12.0
[183] BiocVersion_3.19.1 evaluate_1.0.1
[185] SeuratObject_5.0.2 scran_1.32.0
[187] BiocManager_1.30.25 foreach_1.5.2
[189] httpuv_1.6.15 RANN_2.6.2
[191] tidyr_1.3.1 purrr_1.0.2
[193] polyclip_1.10-7 future_1.34.0
[195] clue_0.3-65 scattermore_1.2
[197] ggplot2_3.5.1 rsvd_1.0.5
[199] xtable_1.8-4 RSpectra_0.16-2
[201] later_1.3.2 viridisLite_0.4.2
[203] tibble_3.2.1 memoise_2.0.1
[205] AnnotationDbi_1.66.0 IRanges_2.38.1
[207] cluster_2.1.6 globals_0.16.3
LS0tCnRpdGxlOiAiPENFTlRFUj5FQkFJSSBuMSAyMDI0IDogU0lOR0xFIENFTEwgQU5BTFlTSVMgVFJBSU5JTkc8QlI+IDxCPlBST0NFU1NJTkcgKElJKTwvQj48QlI+RGltZW5zaW9uIHJlZHVjdGlvbiAmIHZpc3VhbGl6YXRpb248L0NFTlRFUj4iCmRhdGU6ICIyMDI0LTExLTE3LjIyIgphdXRob3I6ICJFQkFJSSBuMSBzY1JOQXNlcSBUZWFtIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDogCiAgICBiYWNrZ3JvdW5kOiBibGFjawogICAgZmlnX2hlaWdodDogNgogICAgZmlnX3dpZHRoOiA4CiAgICBoaWdobGlnaHQ6IHRhbmdvICAjIyBUaGVtZSBmb3IgdGhlIGNvZGUgY2h1bmtzCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUgICMjIEFkZHMgbnVtYmVyIHRvIGhlYWRlcnMgKHNlY3Rpb25zKQogICAgdGhlbWU6IGZsYXRseSAgIyMgQ1NTIHRoZW1lIGZvciB0aGUgSFRNTCBwYWdlCiAgICB0b2M6IHRydWUgICMjIEFkZHMgYSB0YWJsZSBvZiBjb250ZW50CiAgICB0b2NfZmxvYXQ6ICAjIyBUT0Mgb3B0aW9ucwogICAgICBjb2xsYXBzZWQ6IHRydWUgICMjIEJ5IGRlZmF1bHQsIHRoZSBUT0MgaXMgZm9sZGVkCiAgICAgIHNtb290aF9zY3JvbGw6IHRydWUgIyMgU21vb3RoIHNjcm9sbCBvZiB0aGUgSFRNTCBwYWdlCiAgICBzZWxmX2NvbnRhaW5lZDogdHJ1ZSAjIyBJbmNsdWRlcyBhbGwgcGxvdHMvaW1hZ2VzIHdpdGhpbiB0aGUgSFRNTAogICAgY29kZV9kb3dubG9hZDogdHJ1ZSAjIyBBZGRzIGEgYnV0dG9uIHRvIGRvd25sb2FkIHRoZSBSbWQKICAgIGNvZGVfZm9sZGluZzogc2hvdwogICAgdGh1bWJuYWlsczogZmFsc2UKICAgIGxpZ2h0Ym94OiB0cnVlCiAgICBmaWdfY2FwdGlvbjogZmFsc2UKICAgIGdhbGxlcnk6IHRydWUKICAgIHVzZV9ib29rZG93bjogdHJ1ZQphbHdheXNfYWxsb3dfaHRtbDogdHJ1ZSAjIyBBbGxvdyBwbGFpbiBIVE1MIGNvZGUgaW4gdGhlIFJtZAplZGl0b3Jfb3B0aW9uczogCiAgbWFya2Rvd246IAogICAgd3JhcDogNzIKLS0tCgoKPCEtLSBBZGQgdGhlIFJvc2NvZmYgYmFubmVyIC0tPgoKYGBge2NzcywgZWNobyA9IEZBTFNFfQpib2R5IHsKICBiYWNrZ3JvdW5kLWltYWdlOiB1cmwoJ2ViYWlpX2Jhbm5lci5wbmcnKTsKICBiYWNrZ3JvdW5kLXJlcGVhdDogbm8tcmVwZWF0OwogIGJhY2tncm91bmQtc2l6ZTogMTAwJTsKfQoKZGl2IHsKICBiYWNrZ3JvdW5kLWNvbG9yOiByZ2JhKDI1NSwgMjU1LCAyNTUsIDAuMzUpICAgLyogMzUlIG9wYXF1ZSB3aGl0ZSAqLzsKICBwYWRkaW5nOiAwLjI1ZW07Cn0KYGBgCgo8IS0tIEFsbG93cyB0byBoaWRlIHRoZSBUT0MgYnkgZGVmYXVsdCwgZGlzcGxheSBpdCB3aXRoIGEgYnV0dG9uLCBtb3ZlIGl0IHRvIHRoZSByaWdodCBvciBsZWZ0IG9mIHRoZSBwYWdlIC0tPgpgciBIbWlzYzo6aGlkaW5nVE9DKGJ1dHRvbkxhYmVsID0gJ1Nob3cgVE9DJywgaGlkZGVuID0gVFJVRSwgdG9jU2lkZSA9ICdsZWZ0JywgYnV0dG9uU2lkZT0nbGVmdCcsIHBvc0NvbGxhcHNlID0gJ21hcmdpbicsIGxldmVscyA9IDMpYAoKCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQojIG9wdGlvbnMod2lkdGggPSA2MCk7CmtuaXRyOjpvcHRzX2NodW5rJHNldCgKICBlY2hvID0gVFJVRSwgICAgICAgICMgUHJpbnQgdGhlIGNvZGUKICBldmFsID0gVFJVRSwgICAgICAgIyBEbyBub3QgcnVuIGNvbW1hbmQgbGluZXMKICBtZXNzYWdlID0gRkFMU0UsICAgICMgUHJpbnQgbWVzc2FnZXMKICBwcm9tcHQgPSBGQUxTRSwgICAgICMgRG8gbm90IGRpc3BsYXkgcHJvbXB0CiAgY29tbWVudCA9IE5BLCAgICAgICAjIE5vIGNvbW1lbnRzIG9uIHRoaXMgc2VjdGlvbgogIHdhcm5pbmcgPSBGQUxTRSwgICAgIyBEaXNwbGF5IHdhcm5pbmdzCiAgdGlkeSA9IEZBTFNFLAogIGZpZy5hbGlnbiA9ICJjZW50ZXIiLAogICMgcmVzdWx0cyA9ICdoaWRlJwogIHdpZHRoID0gMTAwICAgICAgICMgTnVtYmVyIG9mIGNoYXJhY3RlcnMgcGVyIGxpbmUKKQpgYGAKCgpgYGB7ciBrbml0X2hvb2ssIGVjaG8gPSBGQUxTRX0KaG9va3MgPSBrbml0cjo6a25pdF9ob29rcyRnZXQoKQpob29rX2ZvbGRhYmxlID0gZnVuY3Rpb24odHlwZSkgewogIGZvcmNlKHR5cGUpCiAgZnVuY3Rpb24oeCwgb3B0aW9ucykgewogICAgcmVzID0gaG9va3NbW3R5cGVdXSh4LCBvcHRpb25zKQogICAgCiAgICBpZiAoaXNGQUxTRShvcHRpb25zW1twYXN0ZTAoImZvbGQuIiwgdHlwZSldXSkpIHJldHVybihyZXMpCiAgICAKICAgIHBhc3RlMCgKICAgICAgIjxkZXRhaWxzPjxzdW1tYXJ5PlNob3cgIiwgdHlwZSwgIjwvc3VtbWFyeT5cblxuIiwKICAgICAgcmVzLAogICAgICAiXG5cbjwvZGV0YWlscz4iCiAgICApCiAgfQp9CmtuaXRyOjprbml0X2hvb2tzJHNldCgKICBvdXRwdXQgPSBob29rX2ZvbGRhYmxlKCJvdXRwdXQiKSwKICBwbG90ID0gaG9va19mb2xkYWJsZSgicGxvdCIpCikKYGBgCgo8IS0tIENTUyB0byBjb2xvciBjaHVua3MgYW5kIG91dHB1dHMgLS0+CgpgYGB7Y3NzLCBlY2hvPUZBTFNFfQoubm90cnVuIHsKICBiYWNrZ3JvdW5kLWNvbG9yOiBsaWdodGdyZXkgIWltcG9ydGFudDsKICBib3JkZXI6IDNweCBzb2xpZCBibGFjayAhaW1wb3J0YW50Owp9Ci5ub3RydW5vIHsKICBiYWNrZ3JvdW5kLWNvbG9yOiBsaWdodGdyZXkgIWltcG9ydGFudDsKICBjb2xvciA6IGJsYWNrICFpbXBvcnRhbnQ7Cn0KLnF1ZXN0aW9uIHsKICBiYWNrZ3JvdW5kLWNvbG9yOiBhcXVhbWFyaW5lICFpbXBvcnRhbnQ7CiAgY29sb3IgOiBibGFjayAhaW1wb3J0YW50OwogIGJvcmRlcjogM3B4IHNvbGlkIGxpbWVncmVlbiAhaW1wb3J0YW50Owp9Ci5xdWVzdGlvbm8gewogIGJhY2tncm91bmQtY29sb3I6IGFxdWFtYXJpbmUgIWltcG9ydGFudDsKICBjb2xvciA6IGJsYWNrICFpbXBvcnRhbnQ7Cn0KLmFuc3dlciB7CiAgYmFja2dyb3VuZC1jb2xvcjogbmF2YWpvd2hpdGUgIWltcG9ydGFudDsKICBib3JkZXI6IDNweCBzb2xpZCBicm93biAhaW1wb3J0YW50Owp9Ci5hbnN3ZXJvIHsKICBiYWNrZ3JvdW5kLWNvbG9yOiBuYXZham93aGl0ZSAhaW1wb3J0YW50OwogIGNvbG9yIDogYmxhY2sgIWltcG9ydGFudDsKfQouYmV5b25kIHsKICBiYWNrZ3JvdW5kLWNvbG9yOiB2aW9sZXQgIWltcG9ydGFudDsKICBib3JkZXI6IDNweCBzb2xpZCBwdXJwbGUgIWltcG9ydGFudDsKfQouYmV5b25kbyB7CiAgYmFja2dyb3VuZC1jb2xvcjogdmlvbGV0ICFpbXBvcnRhbnQ7CiAgY29sb3IgOiBibGFjayAhaW1wb3J0YW50Owp9CmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgUFJFQU1CTEUKCiMjIFB1cnBvc2Ugb2YgdGhpcyBzZXNzaW9uCgpUaGlzIGZpbGUgZGVzY3JpYmVzIHRoZSBkaWZmZXJlbnQgc3RlcHMgdG8gcGVyZm9ybSAqKmZpZnRoKiogcGFydCBvZiBkYXRhIHByb2Nlc3NpbmcgZm9yIHRoZSBzaW5nbGUgY2VsbCBSTkFzZXEgZGF0YSBhbmFseXNpcyB0cmFpbmluZyBjb3Vyc2UgZm9yIHRoZSAqKkVCQUlJIG4xCjIwMjQqKiwgY292ZXJpbmcgdGhlc2Ugc3RlcHMgOgoKLSAgICoqRGltZW5zaW9uIHJlZHVjdGlvbioqIG9mIHRoZSBleHByZXNzaW9uIGRhdGEKCi0gICAqKlZpc3VhbGl6YXRpb24qKiBvZiBjZWxscyBleHByZXNzaW9uIGluIGEgMi1EIHNwYWNlCgotICAgVW5zdXBlcnZpc2VkICoqY2x1c3RlcmluZyoqIG9mIGNlbGxzCgotICAgKipEZXNjcmlwdGlvbioqIG9mIHRoZSBkZWZpbmVkIGNsdXN0ZXJzCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBTdGFydCBSc3R1ZGlvCgotICAgVXNpbmcgdGhlIFtPcGVuT25EZW1hbmQgY2hlYXQKICAgIHNoZWV0XShodHRwczovL2lmYi1lbGl4aXJmci5naXRodWIuaW8vRUJBSUkvMjAyMy9lYmFpaW4xL1NpbmdsZUNlbGwvMjAyNF9URF9PcGVuT25EZW1hbmQuaHRtbCksCiAgICBjb25uZWN0IHRvIHRoZSBbT3Blbk9uRGVtYW5kCiAgICBwb3J0YWxdKGh0dHBzOi8vb25kZW1hbmQuY2x1c3Rlci5mcmFuY2UtYmlvaW5mb3JtYXRpcXVlLmZyKSBhbmQKICAgICoqY3JlYXRlIGEgUnN0dWRpbyBzZXNzaW9uKiogd2l0aCB0aGUgcmlnaHQgcmVzb3VyY2UgcmVxdWlyZW1lbnRzLgoKIyBXYXJtLXVwCgotICAgV2Ugc2V0ICoqY29tbW9uIHBhcmFtZXRlcnMqKiB3ZSB3aWxsIHVzZSB0aHJvdWdob3V0IHRoaXMgc2Vzc2lvbiA6CgpgYGB7ciBzZXRwYXJhbX0KIyMgU2VlZCBmb3IgdGhlIFJORwpteV9zZWVkIDwtIDEzMzdMCgojIyBEaW1lbnNpb25zIHRvIGtlZXAgZnJvbSBkaW1lbnNpb24gcmVkdWN0aW9uCm5fZGltIDwtIDIwCgojIyBSZXNvbHV0aW9uIGZvciBMb3V2YWluIGNsdXN0ZXJpbmcKbF9yZXMgPC0gLjgKYGBgCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBQcmVwYXJlIHRoZSBkYXRhIHN0cnVjdHVyZQoKV2Ugd2lsbCBkbyB0aGUgc2FtZSBhcyBmb3IgZm9ybWVyIHN0ZXBzLCBqdXN0IGNoYW5naW5nIHRoZSBzZXNzaW9uIG5hbWUKOgoKIyMgTWFpbiBkaXJlY3RvcnkKCmBgYHtyIG1haW5kaXIsIGZvbGQub3V0cHV0ID0gRkFMU0V9CiMjIFNldHRpbmcgdGhlIHByb2plY3QgbmFtZQpwcm9qZWN0X25hbWUgPC0gImViYWlpX3NjX3RlYWNoZXJzIiAgIyBEbyBub3QgY29weS1wYXN0ZSB0aGlzICEgSXQncyBNWSBwcm9qZWN0ICEhCgojIyBQcmVwYXJpbmcgdGhlIHBhdGgKVERfZGlyIDwtIHBhc3RlMCgiL3NoYXJlZC9wcm9qZWN0cy8iLCBwcm9qZWN0X25hbWUsICIvU0NfVEQiKQoKIyMgQ3JlYXRpbmcgdGhlIHJvb3QgZGlyZWN0b3J5CmRpci5jcmVhdGUocGF0aCA9IFREX2RpciwgcmVjdXJzaXZlID0gVFJVRSkKCiMjIFByaW50IHRoZSByb290IGRpcmVjdG9yeSBvbi1zY3JlZW4KcHJpbnQoVERfZGlyKQpgYGAKCiMjIEN1cnJlbnQgc2Vzc2lvbgoKYGBge3Igc2Vzc2lvbmRpciwgZm9sZC5vdXRwdXQgPSBGQUxTRX0KIyMgQ3JlYXRpbmcgdGhlIHNlc3Npb24gKFByZXByb2MuMikgZGlyZWN0b3J5CnNlc3Npb25fZGlyIDwtIHBhc3RlMChURF9kaXIsICIvMDVfUHJvYy4yIikKZGlyLmNyZWF0ZShwYXRoID0gc2Vzc2lvbl9kaXIsIHJlY3Vyc2l2ZSA9IFRSVUUpCgojIyBQcmludCB0aGUgc2Vzc2lvbiBkaXJlY3Rvcnkgb24tc2NyZWVuCnByaW50KHNlc3Npb25fZGlyKQpgYGAKCiMjIElucHV0IGRpcmVjdG9yeQoKYGBge3IgaW5kaXIsIGZvbGQub3V0cHV0ID0gRkFMU0V9CiMjIENyZWF0aW5nIHRoZSBJTlBVVCBkYXRhIGRpcmVjdG9yeQppbnB1dF9kaXIgPC0gcGFzdGUwKHNlc3Npb25fZGlyLCAiL0RBVEEiKQpkaXIuY3JlYXRlKHBhdGggPSBpbnB1dF9kaXIsIHJlY3Vyc2l2ZSA9IFRSVUUpCgojIyBQcmludCB0aGUgaW5wdXQgZGlyZWN0b3J5IG9uLXNjcmVlbgpwcmludChpbnB1dF9kaXIpCmBgYAoKIyMgT3V0cHV0IGRpcmVjdG9yeQoKYGBge3Igb3V0ZGlyLCBmb2xkLm91dHB1dCA9IEZBTFNFfQojIyBDcmVhdGluZyB0aGUgT1VUUFVUIGRhdGEgZGlyZWN0b3J5Cm91dHB1dF9kaXIgPC0gcGFzdGUwKHNlc3Npb25fZGlyLCAiL1JFU1VMVFMiKQpkaXIuY3JlYXRlKHBhdGggPSBvdXRwdXRfZGlyLCByZWN1cnNpdmUgPSBUUlVFKQoKIyMgUHJpbnQgdGhlIG91dHB1dCBkaXJlY3Rvcnkgb24tc2NyZWVuCnByaW50KG91dHB1dF9kaXIpCmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgUmVsb2FkIHRoZSBTZXVyYXQgT2JqZWN0CgotICAgV2UgY2FuIHJlbG9hZCB0aGUgb2JqZWN0IHdlIHNhdmVkIGF0IHRoZSBmb3JtZXIgc3RlcAoKYGBge3IgZGF0YWxvYWR9CiMjIFRoZSBsYXRlc3QgU2V1cmF0IG9iamVjdCBzYXZlZCBhcyBSRFMgKG5hbWUpCnNvYmpfZmlsZSA8LSAiMDhfVEQzQV9TNV9TY2FsZWQuMmtfUmVnLlBDcmJfMTI1MDguNDAzNS5SRFMiCgojIyBUaGUgbGF0ZXN0IFNldXJhdCBvYmplY3Qgc2F2ZWQgYXMgUkRTIChmdWxsIHBhdGgpCnNvYmpfcGF0aCA8LSBwYXN0ZTAoVERfZGlyLCAKICAgICAgICAgICAgICAgICAgICAiLzA0X1Byb2MuMS9SRVNVTFRTLyIsCiAgICAgICAgICAgICAgICAgICAgc29ial9maWxlKQoKZm9yY2UgPC0gRkFMU0UgICMjIFRvIGZvcmNlIGEgcmUtZG93bmxvYWQgb2YgYSBaZW5vZG8taG9zdGVkIGJhY2t1cApsb2NhbCA8LSBGQUxTRSAgIyMgVG8gZm9yY2UgYSBsb2FkaW5nIGZyb20gYSBsb2NhbCBiYWNrdXAKCiMjIEluIGNhc2Ugb2YgZXJyb3IvbG9zdCBkYXRhIDogZm9yY2UgYSByZWxvYWQgZnJvbSBhIFplbm9kbyBiYWNrdXAgcmVwb3NpdG9yeQppZihmb3JjZSkgewogIHplbl9pZCA8LSAiMTQwMzUyOTMiCiAgemVuX2JhY2t1cF9maWxlIDwtIHBhc3RlMCgiaHR0cHM6Ly96ZW5vZG8ub3JnL3JlY29yZHMvIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHplbl9pZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICIvZmlsZXMvIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNvYmpfZmlsZSkKICBkb3dubG9hZC5maWxlKHVybCA9IHplbl9iYWNrdXBfZmlsZSwKICAgICAgICAgICAgICAgIGRlc3RmaWxlID0gc29ial9wYXRoKQp9CgojIyBJbiBjYXNlIG9mIGVycm9yL2xvc3QgZGF0YSA6IGZvcmNlIGEgcmVsb2FkIGZyb20gYSBsb2NhbCBiYWNrdXAgcmVwb3NpdG9yeQppZihsb2NhbCkgewogIHNvYmpfcGF0aCA8LSBwYXN0ZTAoCiAgICAiL3NoYXJlZC9wcm9qZWN0cy8yNDIyX2ViYWlpX24xL2F0ZWxpZXJfc2NybmFzZXEvVEQvQkFDS1VQL1JEUy8iLAogICAgc29ial9maWxlKQp9CgojIyBMb2FkIHRoZSBvYmplY3QKc29iaiA8LSByZWFkUkRTKGZpbGUgPSBzb2JqX3BhdGgpCmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgRGltZW5zaW9uIHJlZHVjdGlvbgoKVGhpcyBzdGVwIG9yaWdpbmF0ZXMgZnJvbSB0aGUgb2JzZXJ2YXRpb24gdGhhdCB3ZSBkbyBub3Qgd2FudCBub3IgbmVlZCB0byBjaGFyYWN0ZXJpemUgKiplYWNoKiogb2Ygb3VyICoqdGhvdXNlbmRzIG9mIGNlbGxzKiosIGJ1dCAqKmdyb3VwcyoqIG9mIHRoZW0gKGNsdXN0ZXJzID8gY2VsbCB0eXBlcyA/IG90aGVyID8pLiBUaHVzLCB3ZSBkbyBubyBuZWVkIGFsbCBkYXRhLCBhbmQgZXZlbiBtYXkgYmVuZWZpdCBmcm9tIHN1Y2ggYSByZWR1Y3Rpb24gOgoKKiBSZWR1Y2UgdGhlIGRhdGEgY29tcGxleGl0eQogICogRm9yIGludGVycHJldGF0aW9uCiAgKiBGb3IgY29tcHV0YXRpb25zCiogSW5jcmVhc2UgdGhlIHF1YWxpdHkgb2YgaW5mb3JtYXRpb24gY29udGFpbmVkIGluIHRoZSBkYXRhCiAgKiAqKkVucmljaGluZyoqICJnb29kIGJpb2xvZ2ljYWwgKipzaWduYWxzKioiCiAgKiAqKkRpc2NhcmRpbmcgbm9pc2UqKiAvIGNlbGwtc3BlY2lmaWMgc2lnbmFscwoKVGhlcmUgaXMgYSAqKm11bHRpdHVkZSBvZiBtZXRob2RzKiogZm9yIGRpbWVuc2lvbiByZWR1Y3Rpb24KPGNlbnRlcj4hW10oZGltcmVkX3RyZWUucG5nKTwvY2VudGVyPgoKCiMjIFByaW5jaXBhbCBDb21wb25lbnQgQW5hbHlzaXMgKFBDQSkKCkhlcmUsIHdlIHdpbGwgdXNlIHRoZSBncmFuZC1tb3RoZXIgb2YgYWxsIDogdGhlIFBDQSAoUHJpbmNpcGFsIENvbXBvbmVudCBBbmFseXNpcykKCmBgYHtyIGhfUnVuUENBLCBldmFsID0gRkFMU0V9Cj9TZXVyYXQ6OlJ1blBDQQpgYGAKCioqUXVlc3Rpb25zIDog4q2N4q2NIExpZ2h0bmluZyBxdWl6eiDirY3irY0gKiogOiAKCmBgYHtyIHFfcGNhMSwgY2xhc3Muc291cmNlPSJxdWVzdGlvbiIsIGV2YWwgPSBGQUxTRX0KSG93IG1hbnkgcHJpbmNpcGFsIGNvbXBvbmVudHMgKFBDKSB3aWxsIGJlIGdlbmVyYXRlZCBieSBkZWZhdWx0ID8KYGBgCgo8YnI+CgpgYGB7ciBhX3BjYTEsIGNsYXNzLnNvdXJjZSA9IGMoImZvbGQtaGlkZSIsICJhbnN3ZXIiKSwgZXZhbCA9IEZBTFNFfQojIyAuIFRoZSBhbnN3ZXIgaXMgNTAgKG5wY3MgcGFyYW1ldGVyKQojIwojIyAuIFdlIHdpbGwgdXNlIHRoaXMgZGVmYXVsdCB2YWx1ZS4KIyMKIyMgLiBXYXJuaW5nIDogaW4gc29tZSAocmFyZSkgY29udGV4dHMsIHRoaXMKIyMgICBtYXkgbm90IGJlIGVub3VnaCAhCmBgYAoKPGJyPgoKYGBge3IgcV9wY2EyLCBjbGFzcy5zb3VyY2U9InF1ZXN0aW9uIiwgZXZhbCA9IEZBTFNFfQpXaGljaCBkYXRhIHR5cGUgKGllLCB3aGljaCBTZXVyYXQgb2JqZWN0IGxheWVyKSB3aWxsIGJlIHVzZWQgCnRvIGdlbmVyYXRlIHRoZSBjb21wb25lbnRzID8KYGBgCgo8YnI+CgpgYGB7ciBhX3BjYTIsIGNsYXNzLnNvdXJjZSA9IGMoImZvbGQtaGlkZSIsICJhbnN3ZXIiKSwgZXZhbCA9IEZBTFNFfQojIyAuIERhdGEgZnJvbSB0aGUgc2NhbGUuZGF0YSBsYXllciB3aWxsIGJlIHVzZWQKIyMKIyMgLiBUaGlzIGlzIHVuZm9ydHVuYXRlbHkgbm90IGV4cGxpY2l0CiMjICAgZnJvbSB0aGUgU2V1cmF0OjpSdW5QQ0EgaGVscCBwYWdlICEKYGBgCgo8YnI+CgpQZXJmb3JtIFBDQSBvbiBvdXIgZGF0YQoKYGBge3IgUENBfQojIyBOb3RlIDogYSBzZWVkIGlzIHVzZWQgaGVyZSAhCnNvYmogPC0gU2V1cmF0OjpSdW5QQ0EoCiAgb2JqZWN0ID0gc29iaiwgCiAgYXNzYXkgPSAnUk5BJywgCiAgc2VlZC51c2UgPSBteV9zZWVkLCAKICB2ZXJib3NlID0gRkFMU0UpCmBgYAoKPGJyPgoKRGVzY3JpcHRpb24gOgoKYGBge3IgUENBZGVzYywgY2xhc3Muc291cmNlPSJub3RydW4iLCBjbGFzcy5vdXRwdXQ9Im5vdHJ1bm8ifQpTQy5oZWxwZXI6OlNldXJhdE9iamVjdF9kZXNjcmlwdG9yKHNvYmogPSBzb2JqLCBkZXNjcmliZSA9ICdkaW1yZWQnKQpgYGAKCjxicj4KClZpc3VhbGl6YXRpb24gb2YgdGhlIHZlcnkgZmlyc3QgKip0d28gY29tcG9uZW50cyoqLCB3aXRoIGNlbGxzIGNvbG9yaW5nIGFjY29yZGluZyB0byB0aGUgZXN0aW1hdGVkICoqY2VsbCBjeWNsZSBwaGFzZSoqIDoKCmBgYHtyIFBDQXBsb3R9CiMjIFNjYXR0ZXIgcGxvdCBhbG9uZyBkaW1lbnNpb25zClNldXJhdDo6RGltUGxvdCgKICBvYmplY3QgPSBzb2JqLCAKICAjIyBGaXJzdCB0d28gY29tcG9uZW50cwogIGRpbXMgPSBjKDEsMiksIAogICMjIENvbG9yIGRvdHMgcGVyIGNlbGwgcGhhc2UgZ3JvdXBzCiAgZ3JvdXAuYnkgPSAnQ0NfU2V1cmF0X1BoYXNlJywgCiAgIyMgRGF0YSB0byB1c2UKICByZWR1Y3Rpb24gPSAncGNhJykKYGBgCgo8YnI+CgojIyBRdWVzdGlvbnMKCmBgYHtyIHFfcGNhMywgY2xhc3Muc291cmNlPSJxdWVzdGlvbiIsIGV2YWwgPSBGQUxTRX0KR2l2ZSB1cyB5b3VyIGludGVycHJldGF0aW9uIC8gZmVlbGluZ3MgZnJvbSB0aGlzIHBsb3QgIQpgYGAKCjxicj4KCmBgYHtyIHFfcGNhNCwgY2xhc3Muc291cmNlPSJxdWVzdGlvbiIsIGV2YWwgPSBGQUxTRX0KU2hvdWxkIHdlIGxpbWl0IG91cnNlbHZlcyB0byB1c2luZyAyIGRpbWVuc2lvbnMgdG8gaW50ZXJwcmV0IG91ciBkYXRhID8KYGBgCgo8YnI+PGJyPjxicj4KCi0gICBNYXliZSB3ZSBzaG91ZCAqKnJlZHVjZSoqIGluZm9ybWF0aW9uIGEgdGFkICoqbW9yZSoqLCBqdXN0IGZvciB0aGUgc2FrZSBvZiAuLi4gCgogICAgLSAgIC4uLiB1bmRlcnN0YW5kaW5nIG91ciBkYXRhIC4uLgoKICAgIC0gICAuLi53aXRoIG91ciAqKnBvb3IgaHVtYW4gYnJhaW5zKiogLi4uCgogICAgLSAgIC4uLiBib3JuIGFuZCByYWlzZWQgaW4gYSAzRCAqKmV1Y2xpZGVhbiB3b3JsZCoqICEKCjwhLS0gQVNDSUkgQVJUIEdFTkVSQVRPUiBJJ00gV0lUSCBTVE9PUElEIC0tPgoKYGBge3Igc3Rvb3BpZCwgY2xhc3Muc291cmNlID0gYygiZm9sZC1oaWRlIiwgIm5vdHJ1biIpfQojIwojIyAgICAgX18gICAgICAgICAgICAgIC5fX18vXCAgICAgICAgICAgICAgICAuX18gIF9fICAuX18gICAgICAgICAgICAgICAgX18gICAgICAgICAgICAgICAuX18gICAgLl9fXyAuXy4KIyMgICAgLyAvICAgX19fX19fICAgICB8ICAgKS9fX19fXyAgIF9fICBfICBffF9ffC8gIHxffCAgfF9fICAgICBfX19fX19fLyAgfF8gX18gX19fX19fX18gfF9ffCBfX3wgXy8gfCB8CiMjICAgLyAvICAgL19fX19fLyAgICAgfCAgIHwvICAgICBcICBcIFwvIFwvIC8gIFwgICBfX1wgIHwgIFwgICAvICBfX18vXCAgIF9fXCAgfCAgXF9fX18gXHwgIHwvIF9fIHwgIHwgfAojIyAgIFwgXCAgIC9fX19fXy8gICAgIHwgICB8ICBZIFkgIFwgIFwgICAgIC98ICB8fCAgfCB8ICAgWSAgXCAgXF9fXyAgXCB8ICB8IHwgIHwgIC8gIHxfPiA+ICAvIC9fLyB8ICAgXHwKIyMgICAgXF9cICAgICAgICAgICAgICB8X19ffF9ffF98ICAvICAgXC9cXy8gfF9ffHxfX3wgfF9fX3wgIC8gL19fX18gIC8gfF9ffCB8X19fXy98ICAgX18vfF9fXF9fX18gfCAgIF9fCiMjICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFwvICAgICAgICAgICAgICAgICAgICAgICAgXC8gICAgICAgXC8gICAgICAgICAgICAgfF9ffCAgICAgICAgICAgXC8gICBcLwojIwpgYGAKCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBWaXN1YWxpemF0aW9uCgpUaGlzIGZpbmFsIHByb2Nlc3Npbmcgc3RlcCBuZWVkIHRvIGZpbmFsbHkgKipvYnNlcnZlKiogb3VyIGRhdGEgcmVxdWlyZXMgYSBub3ZlbCBkaW1lbnNpb24gcmVkdWN0aW9uIG1ldGhvZCB3aXRoIGEgdmVyeSBoaWdoIGNoYWxsZW5nZSB0byBvdmVyY29tZSA6IHJlZHVjZSBhIHNwYWNlIG9mIGRvemVucyBvZiBkaW1lbnNpb25zIHRvICoqanVzdCBhIGZldyoqICEKCldlIHdpbGwgdXNlIHRoZSBbKipVTUFQKipdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1VNQVApe3RhcmdldD0iX2JsYW5rIn0gbWV0aG9kLgoKIyMgVW5pZm9ybSBNYW5pZm9sZCBBcHByb3hpbWF0aW9uIGFuZCBQcm9qZWN0aW9uIChVTUFQKQoKSG93ID8KCmBgYHtyIGh1bWFwLCBjbGFzcy5zb3VyY2U9Im5vdHJ1biIsIGNsYXNzLm91dHB1dD0ibm90cnVubyIsIGV2YWwgPSBGQUxTRX0KP1NldXJhdDo6UnVuVU1BUApgYGAKCiMjIyBTZWxlY3QgZGltZW5zaW9ucwoKV2UgZ2VuZXJhdGVkICoqNTAgUENBIGNvbXBvbmVudHMqKiBmcm9tIG91ciB+MTIgSyBmZWF0dXJlcwoKLSAgIFRoZXNlIDUwIGRpbWVuc2lvbnMgbWF5ICoqbm90IGFsbCoqIGNvbnRhaW4gKip2YWx1YWJsZSoqIGluZm9ybWF0aW9uCgotICAgV2Ugc2hvdWxkIHRyeSBkbyAqKnNlbGVjdCB0aGUgbW9zdCB1c2VmdWwqKiBvbmVzIGFuZCAqKmRpc2NhcmQqKiB0aGUgcmVtYWluaW5nICoqbm9pc2UqKgoKLSAgIEJ1dCAqKmhvdyBtYW55Kiogc2hvdWxkIHdlIGtlZXAgPwoKLSAgICoqUXVlc3Rpb24qKiA6IAoKICAgIGBgYHtyIHFfbmRpbTEsIGNsYXNzLnNvdXJjZT0icXVlc3Rpb24iLCBldmFsID0gRkFMU0V9CiAgICBEbyB5b3UgaGF2ZSBhbiBpZGVhIGFib3V0IHRoaXMgbnVtYmVyID8KICAgIGBgYAogICAgCiAgICA8YnI+CgogICAgYGBge3Igcl9uZGltMSwgY2xhc3Muc291cmNlID0gYygiZm9sZC1oaWRlIiwgImFuc3dlciIpLCBldmFsID0gRkFMU0V9CiAgICAjIyAuIEltcG9zc2libGUgdG8gZ3Vlc3Mgd2l0aCBvdXIgY3VycmVudCBrbm93bGVkZ2UuCiAgICAjIwogICAgIyMgLiBCdXQgd2UgY2FuIGdldCBzb21lIGhlbHAgZnJvbSB0aGUgUENBIGRhdGEgaXRzZWxmCiAgICAjIwogICAgIyMgLiBJZiB5b3Ugc2FpZCBhIHZhbHVlIGFib3ZlIHRoZSA1MCBjb21wb25lbnRzIHdlCiAgICAjIyAgIGdlbmVyYXRlZCBmb3Igb3VyIFBDQSwgeW91IHNob3VsZCB3ZWFyIHRoZQogICAgIyMgICBjb25lIG9mIHNoYW1lICEKICAgIGBgYAogICAgCiAgICA8YnI+CgpUaGVyZSBhcmUgKipzZXZlcmFsKiogbWV0aG9kcyB0byBoZWxwIHVzIGNob29zZS4KCldlIHdpbGwgdXNlIGEgdmVyeSAqKnNpbXBsZSwgZ3JhcGhpY2FsKiogbWV0aG9kIDogdGhlIG9ic2VydmF0aW9uIG9mIHRoZSBhbW91bnQgb2YgZ2xvYmFsIHZhcmlhbmNlIGV4cGxhaW5lZCBieSBlYWNoIGNvbXBvbmVudC4KCmBgYHtyIGhfZWxib3csIGNsYXNzLnNvdXJjZT0ibm90cnVuIiwgY2xhc3Mub3V0cHV0PSJub3RydW5vIiwgZXZhbCA9IEZBTFNFfQo/U2V1cmF0OjpFbGJvd1Bsb3QKYGBgCgo8YnI+CgpBcHBseSBvbiBvdXIgZGF0YSA6CgpgYGB7ciBlbGJvd30KIyMgUGVyZm9ybSB0aGUgImVsYm93IHBsb3QiClNldXJhdDo6RWxib3dQbG90KAogIG9iamVjdCA9IHNvYmosIAogIG5kaW1zID0gNTApCmBgYAoKPGJyPgoKKipRdWVzdGlvbioqIDogCgpgYGB7ciBxX25kaW0yLCBjbGFzcy5zb3VyY2U9InF1ZXN0aW9uIiwgZXZhbCA9IEZBTFNFfQpBbnkgbW9yZSBwcmVjaXNlIGlkZWEsIG5vdyA/CmBgYAoKPGJyPgoKYGBge3Igcl9uZGltMiwgY2xhc3Muc291cmNlID0gYygiZm9sZC1oaWRlIiwgImFuc3dlciIpLCBldmFsID0gRkFMU0V9CiMjIC4gVGhlIGNvbnRyaWJ1dGlvbiB0byB0aGUgdmFyaWFuY2UgKHNkwrIpIHNlZW1zCiMjICAgZ3JlYXRseSByZWR1Y2VkIGFmdGVyIDMwIFBDcy4KIyMKIyMgLiBNYXliZSBzb21ldGhpbmcgYmV0d2VlbiB+MTUgYW5kIH4zMCBzaG91bGQgZG8KIyMgICB0aGUgdHJpY2sgPwpgYGAKCjxicj4KCiMjIyBBc3Nlc3MgZGltZW5zaW9ucwoKVG8gZGVtb25zdHJhdGUgdGhlICoqZWZmZWN0Kiogb2YgdGhlIG51bWJlciBvZiBQQyBkaW1lbnNpb25zIHVzZWQgYXMgaW5wdXQgdG8gdGhlIFVNQVAgZ2VuZXJhdGlvbiwgd2Ugd2lsbCBwZXJmb3JtIGEgY29tcGFyaXNvbiB1c2luZyA0IGRpZmZlcmVudCBhbW91bnRzIG9mIHJldGFpbmVkIFBDcyA6ICoqMywgNywgMjMgYW5kIDQ5KiouCgpgYGB7ciBkaW1fc2VsLCBmaWcud2lkdGggPSAyNCwgZmlnLmhlaWdodCA9IDZ9CiMjIFBDQSBtYXggZGltZW5zaW9ucyB0byBldmFsdWF0ZQpwY2FfZGltcyA8LSBjKDMsIDcsIDIzLCA0OSkKCiMjIERlZmluZSBhIGZ1bmN0aW9uIHRvIGNvbXB1dGUgdGhlIFVNQVAKcGNhX2RpbV9ldmFsIDwtIGZ1bmN0aW9uKG9iamVjdCA9IE5VTEwsIGRpbS5tYXggPSAyLCBteV9zZWVkID0gMTMzN0wpIHsKICBvYmplY3QgPC0gU2V1cmF0OjpSdW5VTUFQKAogICAgb2JqZWN0ID0gb2JqZWN0LCBhc3NheSA9ICJSTkEiLCAKICAgIGdyYXBoLm5hbWUgPSAiUk5BX3NubiIsIAogICAgcmVkdWN0aW9uID0gInBjYSIsIGRpbXMgPSAxOmRpbS5tYXgsIAogICAgc2VlZC51c2UgPSBteV9zZWVkKQogIAogICMjIFBsb3QKICBkcE4gPC0gU2V1cmF0OjpEaW1QbG90KAogICAgb2JqZWN0ID0gb2JqZWN0LCAKICAgIHJlZHVjdGlvbiA9ICd1bWFwJywKICAgIGNvbWJpbmUgPSBUUlVFKSArIGdncGxvdDI6OmdndGl0bGUobGFiZWwgPSBwYXN0ZTAoIkRpbSA6ICIsIGRpbS5tYXgpKQogIAogICMjIENsZWFuCiAgcm0ob2JqZWN0KQogIAogICMjIFJldHVybiB0aGUgcGxvdCBvYmplY3QKICByZXR1cm4oZHBOKQp9CgojIyBSdW4gdGhlIGZ1bmN0aW9uIG9uIG11bHRpcGxlIGRpbWVuc2lvbnMsIGdldCBhIGxpc3Qgb2YgZ2dwbG90cwpwY2FfZXZhbF9yZXMgPC0gbGFwcGx5KFggPSBwY2FfZGltcywKICAgICAgICAgICAgICAgICAgICAgICBGVU4gPSBmdW5jdGlvbihwKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlKCJEaW0gOiAiLCBwKQogICAgICAgICAgICAgICAgICAgICAgICAgcGNhX2RpbV9ldmFsKG9iamVjdCA9IHNvYmosIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpbS5tYXggPSBwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG15X3NlZWQgPSBteV9zZWVkKQogICAgICAgICAgICAgICAgICAgICAgIH0pCgojIyBQbG90IHRoZSBsaXN0IGFsbHRvZ2V0aGVyCnBhdGNod29yazo6d3JhcF9wbG90cyhwY2FfZXZhbF9yZXMsIG5yb3cgPSAxKQpgYGAKCjxicj4KCioqUXVlc3Rpb24qKgoKYGBge3IgcV9uZGltMywgY2xhc3Muc291cmNlPSJxdWVzdGlvbiIsIGV2YWwgPSBGQUxTRX0KQW55IG1vcmUgcHJlY2lzZSBpZGVhLCBub3csIEZPUiBSRUFMID8KYGBgCgo8YnI+CgpgYGB7ciByX25kaW0zLCBjbGFzcy5zb3VyY2UgPSBjKCJmb2xkLWhpZGUiLCAiYW5zd2VyIiksIGV2YWwgPSBGQUxTRX0KIyMgLiBWZXJ5IGZldyBQQ3MgYXJlIG5vdCBhYmxlIHRvIHJldHJpZXZlCiMjICAgYSBzdWZmaWNpZW50bHkgZGVmaW5lZCBzdHJ1Y3R1cmUuCiMjCiMjIC4gVGhlIGRpZmZlcmVuY2VzIGJldHdlZW4gMjUgYW5kIDQ5IGFyZQojIyAgIGxpbWl0ZWQgaW4gdGhlIGdsb2JhbCBzdHJ1Y3R1cmUuCiMjICAgVGhpcyBtYXkgaW1wbHkgdGhhdCB0aGUgYWRkaXRpb25hbAojIyAgIGNvbXBvbmVudHMgYWJvdmUgMjUgZG8gbm90IGFkZCBtb3JlIAojIyAgIGluZm9ybWF0aW9uIChuZWl0aGVyIG1vcmUgbm9pc2UsIGhlcmUpLgpgYGAKCjxicj4KCldlIGNhbiBub3cgcGVyZm9ybSB0aGUgZmluYWwgVU1BUCB3aXRoIHRoZSBQQyBkaW1lbnNpb25zIG9mIHlvdXIgY2hvaWNlLgoKIyMjIENyZWF0ZSB0aGUgVU1BUAoKRm9yIHRoZSBuZXh0IHN0ZXBzIG9mIHRoZSB0cmFpbmluZywgd2Ugd2lsbCB1c2UgKipgciBuX2RpbWBgKiogUENBIGRpbWVuc2lvbnMuCgpgYGB7ciB1bWFwMjB9CiMjIFVzaW5nIDIwIFBDcwojIyBBIHNlZWQgaXMgbmVlZGVkIGhlcmUgIQpzb2JqIDwtIFNldXJhdDo6UnVuVU1BUCgKICAgIG9iamVjdCA9IHNvYmosIGFzc2F5ID0gIlJOQSIsIAogICAgZ3JhcGgubmFtZSA9ICJSTkFfc25uIiwgCiAgICByZWR1Y3Rpb24gPSAicGNhIiwgCiAgICBkaW1zID0gMTpuX2RpbSwgCiAgICBzZWVkLnVzZSA9IG15X3NlZWQpCmBgYAoKPGJyPgoKIyMgQm9udXMgOiAzRCBVTUFQIChERU1PKQoKV2hpbGUgYnkgZGVmYXVsdCBTZXVyYXQ6OlJ1blVNQVAgd2lsbCBwcm9kdWNlIDItZGltZW5zaW9uIHJlZHVjdGlvbnMsIHRoZSBtZXRob2QgY2FuIGdlbmVyYXRlIGZ1cnRoZXIgY29tcG9uZW50cy4KCkRlc3BpdGUgb3VyIGxpbWl0ZWQgYnJhaW4sIHRoaXMgaXMgc29tZXRpbWVzIGludGVyZXN0aW5nIGFuZCB1c2VmdWwgdG8gYXR0ZW1wdCBhIHJlZHVjdGlvbiB0byAzIGRpbWVuc2lvbnMgaW5zdGVhZCBvZiAyLiBUaGlzIGNhbiBiZSB2ZXJ5IGVmZmVjdGl2ZSB3aGVuIGxvb2tpbmcgZm9yIHRyYWplY3Rvcmllcy4KCldlIGNhbiBnZW5lcmF0ZSBhIFVNQVAgd2l0aCAzIGNvbXBvbmVudHMgZnJvbSBgciBuX2RpbWBgIFBDcyA6CgpgYGB7ciB1bWFwMkQyNSwgY2xhc3Muc291cmNlPSJub3RydW4iLCBjbGFzcy5vdXRwdXQ9Im5vdHJ1bm8ifQojIyBVTUFQIGZyb20gMjUgUENzLCAzIGNvbXBvbmVudHMgcmVxdWVzdGVkCnNvYmogPC0gU2V1cmF0OjpSdW5VTUFQKAogIG9iamVjdCA9IHNvYmosIGFzc2F5ID0gJ1JOQScsIAogIGdyYXBoLm5hbWUgPSAnUk5BX3NubicsIAogIHJlZHVjdGlvbiA9ICdwY2EnLCAKICByZWR1Y3Rpb24ubmFtZSA9ICd1bWFwM2QnLAogIGRpbXMgPSAxOm5fZGltLCAKICBzZWVkLnVzZSA9IG15X3NlZWQsCiAgbi5jb21wb25lbnRzID0gMykKCiMjIERpbVBsb3Qgb2YgdGhlIGZpcnN0IDIgVU1BUCBjb21wb25lbnRzClNldXJhdDo6RGltUGxvdCgKICBvYmplY3QgPSBzb2JqLCAKICBkaW1zID0gYygxLDIpLAogIHJlZHVjdGlvbiA9ICd1bWFwM2QnKQpgYGAKCjxicj4KCioqUXVlc3Rpb24qKiA6IAoKYGBge3IgcV91bWFwM2QsIGNsYXNzLnNvdXJjZT0icXVlc3Rpb24iLCBldmFsID0gRkFMU0V9Cklzbid0IHRoZXJlIHNvbWV0aGluZyBzdHJpa2luZyA/CmBgYAoKPGJyPgoKYGBge3IgYV91bWFwM2QsIGNsYXNzLnNvdXJjZSA9IGMoImZvbGQtaGlkZSIsICJhbnN3ZXIiKSwgZXZhbCA9IEZBTFNFfQojIyAuIFRoZSBwbG90IGlzIG5vdCB0aGUgc2FtZSBhcyB3aGVuCiMjICAgdXNpbmcgMjUgUENzIGFuZCByZXF1ZXN0aW5nIDIgVU1BUAojIyAgIGNvbXBvbmVudHMgaW5zdGVhZCBvZiAzIGhlcmUgIQojIyAuIFRoZSAyIGNvbXBvbmVudHMgb2YgYSAyRCBVTUFQIGFyZSBub3QKIyMgICB0aGUgc2FtZSBhcyB0aGUgdHdvIGZpcnN0IGNvbXBvbmVudHMKIyMgICBvZiBhIGRpbT4yIFVNQVAuCmBgYAoKPGJyPgoKTGV0J3MgcGVyZm9ybSBhIDNEIHJlcHJlc2VudGF0aW9uIG9mIG91ciBVTUFQCgpgYGB7ciB1bWFwM0QyNSwgY2xhc3Muc291cmNlPSJub3RydW4iLCBjbGFzcy5vdXRwdXQ9Im5vdHJ1bm8iLCBldmFsID0gRkFMU0V9CiMjIFN0cnVjdHVyZSBkYXRhIHRvIHBsb3QgaW4gYSBkYXRhLmZyYW1lCmRmM2QgPC0gYXMuZGF0YS5mcmFtZSgKICBTZXVyYXQ6OlJlZHVjdGlvbnMob2JqZWN0ID0gc29iaiwgCiAgICAgICAgICAgICAgICAgICAgIHNsb3QgPSAidW1hcDNkIilAY2VsbC5lbWJlZGRpbmdzCiAgKQoKIyMgM0QgcGxvdApwbG90bHk6OnBsb3RfbHkoCiAgZGF0YSA9IGRmM2QsIAogIHggPSB+dW1hcDNkXzEsIAogIHkgPSB+dW1hcDNkXzIsIAogIHogPSB+dW1hcDNkXzMsIAogIHR5cGUgPSAnc2NhdHRlcjNkJywgCiAgbWFya2VyID0gbGlzdChzaXplID0gMiwgd2lkdGg9MikpCmBgYAoKPGJyPgoKPGNlbnRlcj4hW10odGQzYV91bWFwM2QucG5nKTwvY2VudGVyPgoKPGJyPgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCjxicj4KCiMgU2F2ZSB0aGUgU2V1cmF0IG9iamVjdAoKV2Ugd2lsbCBzYXZlIG91ciBTZXVyYXQgb2JqZWN0IHRoYXQgbm93IGNvbnRhaW5zIFBDQSBhbmQgVU1BUCByZWR1Y3Rpb25zICA6CgpgYGB7ciBzYXZlcmRzMSwgZm9sZC5vdXRwdXQgPSBGQUxTRX0KIyMgU2F2ZSBvdXIgU2V1cmF0IG9iamVjdCAocmljaCBuYW1pbmcpCm91dF9uYW1lIDwtIHBhc3RlMCgKICAgICAgICAgIG91dHB1dF9kaXIsICIvIiwgcGFzdGUoCiAgICAgICAgICAgIGMoIjA5IiwgU2V1cmF0OjpQcm9qZWN0KHNvYmopLCAiUzUiLCAKICAgICAgICAgICAgICAiRGltUmVkLlBDQSIsIHBhc3RlKAogICAgICAgICAgICAgICAgZGltKHNvYmopLCAKICAgICAgICAgICAgICAgIGNvbGxhcHNlID0gJy4nCiAgICAgICAgICAgICAgKQogICAgICAgICAgICApLCBjb2xsYXBzZSA9ICJfIiksCiAgICAgICAgICAgICIuUkRTIikKCiMjIENoZWNrCnByaW50KG91dF9uYW1lKQoKIyMgV3JpdGUgb24gZGlzawpzYXZlUkRTKG9iamVjdCA9IHNvYmosIAogICAgICAgIGZpbGUgPSBvdXRfbmFtZSkKYGBgCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBDbHVzdGVyaW5nCgpXZSBjYW4gbm93IGF0dGVtcHQgdG8gZGV0ZXJtaW5lIGhvdyBjZWxscyAqKmFyZSBvcmdhbml6ZWQqKiBpbiBhbiAqKnVuc3VwZXJ2aXNlZCoqIG1hbm5lciBpbiB0aGlzIHNwYWNlCgpXZSB3aWxsIHVzZSB0aGUgZ3JhcGgtYmFzZWQgY2x1c3RlcmluZyBtZXRob2QgW0xvdXZhaW5dKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0xvdXZhaW5fbWV0aG9kKXt0YXJnZXQ9Il9ibGFuayJ9CgpDbHVzdGVyaW5nIHdpbGwgYmUgcGVyZm9ybWVkIG9uIHRoZSAqKlBDQSoqIGRpbWVuc2lvbiByZWR1Y3Rpb24sICoqbm90Kiogb24gdGhlIFVNQVAgb25lCgpgYGB7ciBxX2NsdXN0X29uX1BDQSwgY2xhc3Muc291cmNlPSJxdWVzdGlvbiIsIGV2YWwgPSBGQUxTRX0KQW55IGlkZWEgd2h5ID8KYGBgCgo8YnI+CgojIyBGaW5kIG5laWdoYm91cnMKCkJlZm9yZSBydW5uaW5nIHRoZSBMb3V2YWluIG1ldGhvZCwgYSBmaXJzdCBwYXNzIG1ldGhvZCBpcyB1c2VkIHRvIGdlbmVyYXRlIGEgIkstTmVhcmVzdCBOZWlnaGJvdXIiIGdyYXBoIChzZWUgbW9yZSBkZXRhaWxzIFtoZXJlXShodHRwczovL3NhdGlqYWxhYi5vcmcvc2V1cmF0L2FydGljbGVzL3BibWMza190dXRvcmlhbC5odG1sKXt0YXJnZXQ9Il9ibGFuayJ9KS4KCmBgYHtyIGZubjIwfQojIyBDb21wdXRlIGEgU05OIHVzaW5nIHRoZSBmaXJzdCAyMCBQQ3MKc29iaiA8LSBTZXVyYXQ6OkZpbmROZWlnaGJvcnMoCiAgb2JqZWN0ID0gc29iaiwgCiAgZGltcyA9IDE6MjAsIAogIHJlZHVjdGlvbiA9ICJwY2EiKQpgYGAKCiMjIExvdXZhaW4gY2x1c3RlcmluZwoKLSAgIFdlIHdpbGwgdGVzdCAzIGRpZmZlcmVudCByZXNvbHV0aW9ucwoKLSAgIFRoZSBTZXVyYXQgZnVuY3Rpb24gdG8gcGVyZm9ybSBjbHVzdGVyaW5nIGNhbiBiZSBjYWxsZWQgd2l0aCBtdWx0aXBsZSByZXNvbHV0aW9ucyBhdCBvbmNlLgoKYGBge3IgY2x1c3RMMjB9CiMjIExvdXZhaW4gcmVzb2x1dGlvbnMgdG8gdGVzdApyZXNvbCA8LSBjKC4zLCAwLjgsIDEuNSkKCiMjIENsdXN0ZXJpbmcKc29iaiA8LSBTZXVyYXQ6OkZpbmRDbHVzdGVycygKICBvYmplY3QgPSBzb2JqLCAKICByZXNvbHV0aW9uID0gcmVzb2wsCiAgdmVyYm9zZSA9IEZBTFNFKQpgYGAKCjxicj4KCioqUXVlc3Rpb24qKgoKYGBge3IgcV9jbHVzdGRlc2MsIGNsYXNzLnNvdXJjZT0icXVlc3Rpb24iLCBldmFsID0gRkFMU0V9CkNvdWxkIHlvdSB0ZWxsIHVzIHdoYXQgY2hhbmdlZCBpbiBvdXIgb2JqZWN0ID8KYGBgCgo8YnI+CgpgYGB7ciBhX2NsdXN0ZGVzYywgY2xhc3Muc291cmNlPWMoImZvbGQtaGlkZSIsICJhbnN3ZXIiKSwgY2xhc3Mub3V0cHV0PSJhbnN3ZXJvIn0KIyMgT25lIGNhbiBqdXN0IGNhbGwgaXQgOgpzb2JqCiMjIyBIbW1tLCBub3RoaW5nIG5ldyB1bmRlciB0aGUgc3VuIC4uLgoKIyMgT25lIGNhbiBkZXNjcmliZSBpdCA6ClNDLmhlbHBlcjo6U2V1cmF0T2JqZWN0X2Rlc2NyaXB0b3IoCiAgc29iaiA9IHNvYmosCiAgZGVzY3JpYmUgPSAiY29sZGF0YSIpCmBgYAoKPGJyPgoKIyMgVmlzdWFsaXphdGlvbiAmIHNlbGVjdGlvbgoKIyMjIE9uIFVNQVBzCgpQbG90dGluZyBVTUFQcyBoYXJib3JpbmcgdGhlIGNsdXN0ZXJpbmcgcmVzdWx0cyBmb3Igb3VyIDMgdGVzdGVkIHJlc29sdXRpb25zCgpgYGB7ciBjbHVzdF9kaW1wbG90LCBmaWcud2lkdGg9MTgsIGZpZy5oZWlnaHQ9Nn0KIyMgTWV0YWRhdGEgbmFtZSBvZiBjbHVzdGVyaW5nIHJlc3VsdHMKcmVzb2xfbmFtZXMgPC0gcGFzdGUwKCJSTkFfc25uX3Jlcy4iLCByZXNvbCkKClNldXJhdDo6RGltUGxvdCgKICBvYmplY3QgPSBzb2JqLCAKICByZWR1Y3Rpb24gPSAidW1hcCIsIAogIGdyb3VwLmJ5ID0gcmVzb2xfbmFtZXMsCiAgbGFiZWwgPSBUUlVFLCAKICByZXBlbCA9IFRSVUUpCmBgYAoKPGJyPgoKIyMjIENsdXN0ZXJzIGNvbnRpbmdlbmNpZXMgYW5kIHByb3BvcnRpb25zCgpPbmUgY2FuIG9ic2VydmUgaG93IG1hbnkgY2VsbHMgYXJlIGluIGVhY2ggY2x1c3RlciwgYW5kIHdoYXQgcHJvcG9ydGlvbiBvZiBhbGwgY2VsbHMgdGhlc2UgcmVwcmVzZW50CgpgYGB7ciBjbHVzdF9wb3AuOH0KZm9yICh4IGluIHJlc29sX25hbWVzKSB7CiAgIyMgQ29udGluZ2VuY2llcwogIHByaW50KHRhYmxlKHNvYmpbW3hdXSkpCiAgIyMgUHJvcG9ydGlvbnMKICBwcmludChmb3JtYXQodGFibGUoc29ialtbeF1dKSAvIG5jb2woc29iaiksIGRpZ2l0cyA9IDIpKQogIGNhdCgnXG4nKQp9CmBgYAoKPGJyPgoKIyMjIENsdXN0ZXItc3BlY2lmaWMgbWFya2VycwoKQSBwcmFjdGljYWwgd2F5IHRvIGNoYXJhY3Rlcml6ZSBvdXIgY2x1c3RlcmluZyByZXN1bHRzIGlzIHRvIGdldCBiYWNrIHRvIGEgbGV2ZWwgb2Yga25vd2xlZGdlIHlvdSBhcmUgY29uZmlkZW50IGluIDogbWFya2VyIGdlbmVzLgoKU2V1cmF0IGhhcyBhIGhhbmR5IGZ1bmN0aW9uIHRvIDoKCi0gICBJZGVudGlmeSBkaWZmZXJlbnRpYWwgZXhwcmVzc2VkIGdlbmVzIHNwZWNpZmljIHRvIGVhY2ggYW5kIGV2ZXJ5IHByb3ZpZGVkIGNhdGVnb3J5IG9mIGNlbGxzIChoZXJlLCBjbHVzdGVyaW5nIHJlc3VsdHMpCgotICAgRHJhdyBhIGNsdXN0ZXJpemVkLCBhbm5vdGF0ZWQgaGVhdG1hcCBvZiB0aGVzZSBnZW5lcwoKYGBge3IgaF9mYW0sIGNsYXNzLnNvdXJjZT0ibm90cnVuIiwgY2xhc3Mub3V0cHV0PSJub3RydW5vIiwgZXZhbCA9IEZBTFNFfQo/U2V1cmF0OjpGaW5kQWxsTWFya2VycwpgYGAKCmBgYHtyIGRobSwgZmlnLndpZHRoPTE4LCBmaWcuaGVpZ2h0PTZ9CiMjIExvb3Bpbmcgb24gY2x1c3RlcmluZyByZXN1bHRzCmZtYV9hbGwgPC0gbGFwcGx5KHJlc29sX25hbWVzLCBmdW5jdGlvbihyKSB7CiAgCiAgIyMgRmluZCBtYXJrZXJzIGZvciBhbGwgY2x1c3RlcnMKICBTZXVyYXQ6OklkZW50cyhvYmplY3QgPSBzb2JqKSA8LSBzb2JqW1tyXV1bWzFdXQogIGZhbSA8LSBTZXVyYXQ6OkZpbmRBbGxNYXJrZXJzKAogICAgb2JqZWN0ID0gc29iaiwgCiAgICBsb2dmYy50aHJlc2hvbGQgPSAuNSwKICAgIG9ubHkucG9zID0gVFJVRSwgCiAgICBtaW4ucGN0ID0gLjUsIAogICAgdmVyYm9zZSA9IEZBTFNFLAogICAgcmFuZG9tLnNlZWQgPSBteV9zZWVkKQogIAogICMjIFNlbGVjdCB0b3AxMCBnZW5lcyB3aGVuIGF2YWlsYWJsZQogIGZhbV9yZHggPC0gZHBseXI6Omdyb3VwX2J5KC5kYXRhID0gZmFtLCBjbHVzdGVyKQogIGZhbV9yZHggPC0gZHBseXI6OmZpbHRlciguZGF0YSA9IGZhbV9yZHgsIGF2Z19sb2cyRkMgPiAxKQogIGZhbV9yZHggPC0gZHBseXI6OnNsaWNlX2hlYWQoLmRhdGEgPSBmYW1fcmR4LCBuID0gMTApCiAgZGggPC0gU2V1cmF0OjpEb0hlYXRtYXAob2JqZWN0ID0gc29iaiwgZmVhdHVyZXMgPSBmYW1fcmR4JGdlbmUsIGNvbWJpbmUgPSBUUlVFKSArIGdncGxvdDI6OmdndGl0bGUobGFiZWwgPSByKQogIHJldHVybihkaCkKfSkKCiMjIFBsb3QgYWxsIGhlYXRtYXBzIGF0IG9uY2UKcGF0Y2h3b3JrOjp3cmFwX3Bsb3RzKGZtYV9hbGwpICsgcGF0Y2h3b3JrOjpwbG90X2xheW91dChucm93ID0gMSkKCmBgYAoKPGJyPgoKKipRdWVzdGlvbnMqKiA6IENvbXBhcmluZyB0aGUgaGVhdG1hcHMgOiAKCmBgYHtyIHFfaG0xLCBjbGFzcy5zb3VyY2U9InF1ZXN0aW9uIiwgZXZhbCA9IEZBTFNFfQpXaGljaCByZXNvbHV0aW9uIHdvdWxkIHlvdSBjaG9vc2UsIGFuZCB3aHkgPwpgYGAKCjxicj4KCmBgYHtyIHFfaG0yLCBjbGFzcy5zb3VyY2U9InF1ZXN0aW9uIiwgZXZhbCA9IEZBTFNFfQpJcyB0aGVyZSBhIHNpbmdsZSBvbmUgYW5kIG9ubHkgYW5zd2VyIHRvIHRoZSBmb3JtZXIgcXVlc3Rpb24gPwpgYGAKCjxicj4KCiMjIFNlbGVjdGlvbgoKRm9yIHRoZSBkb3duc3RyZWFtIGFuYWx5c2VzLCB3ZSB3aWxsIHVzZSB0aGUgcmVzb2x1dGlvbiAqKmByIGxfcmVzYCoqCgpgYGB7ciBzZWxfcmVzfQpTZXVyYXQ6OklkZW50cyhvYmplY3QgPSBzb2JqKSA8LSBzb2JqW1twYXN0ZTAoIlJOQV9zbm5fcmVzLiIsIGxfcmVzKV1dW1sxXV0KYGBgCgo8YnI+CgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKPGJyPgoKIyBTYXZlIHRoZSBTZXVyYXQgb2JqZWN0CgpXZSB3aWxsIHNhdmUgb3VyIFNldXJhdCBvYmplY3QgdGhhdCBub3cgY29udGFpbnMgb3VyIGNsdXN0ZXJpbmcgcmVzdWx0cyAgOgoKYGBge3Igc2F2ZXJkczIsIGZvbGQub3V0cHV0ID0gRkFMU0V9CiMjIFNhdmUgb3VyIFNldXJhdCBvYmplY3QgKHJpY2ggbmFtaW5nKQpvdXRfbmFtZSA8LSBwYXN0ZTAoCiAgICAgICAgICBvdXRwdXRfZGlyLCAiLyIsIHBhc3RlKAogICAgICAgICAgICBjKCIxMCIsIFNldXJhdDo6UHJvamVjdChzb2JqKSwgIlM1IiwgCiAgICAgICAgICAgICAgcGFzdGUwKAogICAgICAgICAgICAgICAgIkNsdXN0ZXJlZC4iLAogICAgICAgICAgICAgICAgbF9yZXMpLCAKICAgICAgICAgICAgICBwYXN0ZSgKICAgICAgICAgICAgICAgIGRpbShzb2JqKSwgCiAgICAgICAgICAgICAgICBjb2xsYXBzZSA9ICcuJwogICAgICAgICAgICAgICkKICAgICAgICAgICAgKSwgY29sbGFwc2UgPSAiXyIpLAogICAgICAgICAgICAiLlJEUyIpCgojIyBDaGVjawpwcmludChvdXRfbmFtZSkKCiMjIFdyaXRlIG9uIGRpc2sKc2F2ZVJEUyhvYmplY3QgPSBzb2JqLCAKICAgICAgICBmaWxlID0gb3V0X25hbWUpCmBgYAoKPGJyPgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCjxicj48YnI+PGJyPgoKIyBSc2Vzc2lvbgoKYGBge3IgcnNlc3Npb24sIGNsYXNzLnNvdXJjZT0ibm90cnVuIiwgY2xhc3Mub3V0cHV0PSJub3RydW5vIn0KdXRpbHM6OnNlc3Npb25JbmZvKCkKYGBgCg==