PREAMBLE
Purpose of this session
This file describes the different steps to perform fifth part of the data processing for the single cell RNAseq data analysis training course for the EB3I n1 2025, 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 :
# setparam
## Set your project name
# WARNING : Do not just copy-paste this ! It's MY project name ! Put YOURS !!
project_name <- "ebaii_sc_teachers"
## Control if the project_name exists on the cluster
cat('PATH CHECK : ', dir.exists(paste0('/shared/projects/', project_name)))
Show output
PATH CHECK : TRUE
## Seed for the RNG
my_seed <- 1337L
## Known marker genes for TD3A
td3a_markers <- c("Apoe", "Birc5", "Plac8", "Itm2a", "Ptcra", "Trbv29", "Isg15", "Cldn10")
Prepare the data structure
We will do the same as for former steps, just changing the session name
:
Main directory
#maindir
## 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
# sessiondir
## 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
#outdir
## 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
## dataload
## This is the path to the current EB3I backup
sessionid <- '2538_eb3i_n1_2025'
## 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)
## Recreate the expected path if it does not exist
dir.create(path = dirname(sobj_path), recursive = TRUE)
## Download the 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/", sessionid, "/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 :
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)
# h_runPCA
?Seurat::RunPCA()
Questions : ⭍⭍ Lightning quizz ⭍⭍ :
# q_pca1
How many principal components (PC) will be generated by default ?
# a_pca1
## . The answer is 50 (npcs parameter)
##
## . We will use this default value.
##
## . Warning : in some (rare) contexts, this
## may not be enough !
# q_pca2
Which data type (ie, which Seurat object layer) will be used
to generate the components ?
# a_pca2
## . Data from the scale.data layer will be used
##
## . This is unfortunately not really explicit
## from the Seurat::RunPCA help page ! (see the "features"
## part)
##
## . Web search : "which slot is used by RunPCA"
##"
## . When run on a Seurat object without @scale.data layer :
## "Error in GetAssayData(object, assay.type = assay.type, slot = "scale.data") :
## Object@scale.data has not been set. Run ScaleData() and then retry."
Perform PCA on our data
# pca
## Note : a seed is used here !
sobj <- Seurat::RunPCA(
object = sobj,
assay = 'RNA',
seed.use = my_seed,
verbose = FALSE)
Description :
# PCAdesc
## Please, focus on the "reductions" slot
View(sobj)
Visualization of the very first two components, with cells coloring according to the estimated cell cycle phase :
# PCAplot
## 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
# q_pca3
Give us your interpretation / feelings from this plot !
# q_pca4
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 the 20 PCs :
# umap3D20
## UMAP from 20 PCs, 3 components requested
sobj_U3D <- 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)
We can plot the two first UMAP components from this 3D space :
# plot_umap3D20
## DimPlot of the first 2 UMAP components
umap3d <- Seurat::DimPlot(
object = sobj_U3D,
dims = c(1,2),
reduction = 'umap3d') + Seurat::DarkTheme()
print(umap3d)
Show plot

Question :
# q_umap3D
Isn't there something striking ?
# a_umap3D
## . The plot is not the same as when
## using 25 PCs when requesting 3 UMAP
## components instead of 2 !
##
## . The 2 components of a 2D UMAP are not
## the same as the two first components
## of a dim>2 UMAP.
# umapXd
patchwork::wrap_plots(
list(
umap2d & ggplot2::ggtitle(label = "UMAP (2D)"),
umap3d & ggplot2::ggtitle(label = "UMAP (3D)")),
nrow = 1)
Show plot

Let’s perform a 3D representation of our UMAP (PRE-RENDERED)
# umap3D20rgl
## 3D plot (not run as failing in interactive mode)
plotly::plot_ly(
data = as.data.frame(Seurat::Reductions(
object = sobj_U3D,
slot = "umap3d")@cell.embeddings),
x = ~umap3d_1,
y = ~umap3d_2,
z = ~umap3d_3,
type = 'scatter3d',
marker = list(size = 2, width=2))
## Clean
rm(sobj_U3D)
Save the Seurat object
We will save our Seurat object that now contains PCA and UMAP reductions :
# saverds1
## 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 !
# q_clust_on_PCA
Any idea why ?
# a_clust_on_PCA
## . UMAP is a strong APPROXIMATION of the multidimensional space
## which single purpose is to HELP us understand more easily the
## hidden structure of our dataset.
##
## . The PCA component (or any reduction method used directly from our
## normalized/scaled data, like MDS or ICA, ...) contains the "real"
## information, just distributed differently.
##
## . If one uses the "second reduced space" used for visualization (UMAP,
## tSNE, DM, ...) as the basis for clustering, one may obtain pretty plots
## with well-defined clusters. The latter being based on wrong data, thus
## leading to wrong interpretation :(
Find neighbors
Before running the Louvain method, a first pass method is used to generate a “K-Nearest Neighbour” graph (see more details here).
# fnn20
## Compute a SNN using the first 20 PCs
sobj <- Seurat::FindNeighbors(
object = sobj,
dims = 1:20,
reduction = "pca")
Louvain clustering
We will test multiple different resolutions
The Seurat function to perform clustering can be called with
multiple resolutions at once (less to code, lucky us !).
TeAmWoRk TiMe !
We wil dispatch the different clustering resolution values to groups of trainees.
# clustL20
## Louvain resolutions to test
resol <- c(.3, .6, .9, 1.2, 1.5, 1.8)
## Clustering
sobj <- Seurat::FindClusters(
object = sobj,
resolution = resol,
verbose = FALSE)
Question
# q_clustdesc
Could you tell us what changed in our object ?
# a_clustdesc
## Please, focus on the [meta.data] slot
View(sobj)
## . New entries in the barcodes metadata :
## . The results of our clusterings
## . 'seurat_clusters' which corresponds to the
## last version we ran
##
## . This 'seurat_clusters' annotation will be the
## one used by default by Seurat for any further
## analysis
Assess resolutions
On UMAPs
Clusters
Plotting UMAPs harboring the clustering results for our tested resolutions
# clust_dimplot
## Metadata name of clustering results (defined by default by Seurat)
resol_names <- paste0("RNA_snn_res.", resol)
## DimPlot
Seurat::DimPlot(
object = sobj,
reduction = "umap",
group.by = resol_names,
label = TRUE,
repel = TRUE)
Show plot

# q_compres
. Could you briefly compare the different versions ?
. Would you consider that some results are under/over-clustered ?
. Are all clusters well-defined ?
Cell-type markers
We can plot the cell markers we already know
# u_markers
## Multiple FeaturePlots with markers
Seurat::FeaturePlot(object = sobj,
features = td3a_markers) +
patchwork::plot_layout(nrow = 3,
ncol = 3)
Show plot

Just for “fun” : what would have been seen on a PCA ? (DEMO)
# u_markers_pca
## Multiple FeaturePlots with markers
Seurat::FeaturePlot(object = sobj,
features = td3a_markers,
reduction = "pca") +
patchwork::plot_layout(nrow = 3,
ncol = 3)
Show plot

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
# h_fam
?Seurat::FindAllMarkers
# dhm_prep
## Looping on clustering results
fam_all <- lapply(resol_names, function(r) {
## Find markers for all clusters
## A seed is needed here !
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)
## Get top gene per cluster
famtop <- fam$gene[!duplicated(fam$cluster)]
vln_pl <- Seurat::VlnPlot(object = sobj, features = famtop)
print(vln_pl)
## 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, size = 3,
combine = TRUE) + ggplot2::ggtitle(label = r)
return(dh)
})
Show plot

Show plot

Show plot

Show plot

Show plot

Show plot

# dhm_plot
## Plot all heatmaps at once
patchwork::wrap_plots(fam_all) + patchwork::plot_layout(nrow = 2)
Show plot

Questions : Comparing the heatmaps :
# q_hm1
Which resolution would you choose, and why ?
# q_hm2
Is there a single one and only answer to the former question ?
Clusters contingencies and proportions
One can observe how many cells are in each cluster, and what proportion of all cells these represent (DEMO)
# clustpop
## Looping on resolutions
for (x in resol_names) {
## Contingencies
print(table(sobj[[x]]))
## Proportions
print(format(table(sobj[[x]]) / ncol(sobj), digits = 2))
cat('\n\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.6
0 1 2 3 4 5 6 7 8 9
907 779 674 481 421 204 189 159 118 103
RNA_snn_res.0.6
0 1 2 3 4 5 6 7 8 9
"0.225" "0.193" "0.167" "0.119" "0.104" "0.051" "0.047" "0.039" "0.029" "0.026"
RNA_snn_res.0.9
0 1 2 3 4 5 6 7 8 9 10
889 761 683 482 381 216 203 159 119 104 38
RNA_snn_res.0.9
0 1 2 3 4 5 6 7
"0.2203" "0.1886" "0.1693" "0.1195" "0.0944" "0.0535" "0.0503" "0.0394"
8 9 10
"0.0295" "0.0258" "0.0094"
RNA_snn_res.1.2
0 1 2 3 4 5 6 7 8 9 10 11 12
587 508 483 459 399 395 331 253 203 157 117 105 38
RNA_snn_res.1.2
0 1 2 3 4 5 6 7
"0.1455" "0.1259" "0.1197" "0.1138" "0.0989" "0.0979" "0.0820" "0.0627"
8 9 10 11 12
"0.0503" "0.0389" "0.0290" "0.0260" "0.0094"
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"
RNA_snn_res.1.8
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
457 347 329 322 281 258 240 232 218 195 193 171 171 170 155 119 105 38 34
RNA_snn_res.1.8
0 1 2 3 4 5 6 7
"0.1133" "0.0860" "0.0815" "0.0798" "0.0696" "0.0639" "0.0595" "0.0575"
8 9 10 11 12 13 14 15
"0.0540" "0.0483" "0.0478" "0.0424" "0.0424" "0.0421" "0.0384" "0.0295"
16 17 18
"0.0260" "0.0094" "0.0084"
Selection
For the downstream analyses, we will use the resolution 0.8.
We will fix the clustering results for this resolution as the default one
Seurat will use for any further analyses / plots, using the Seurat::Idents()
function.
# sel_res
## Fixing l_res
l_res <- .8
## Performing Louvain clustering at the selected resolution
sobj <- Seurat::FindClusters(
object = sobj,
resolution = l_res,
verbose = FALSE)
## Check on default "Idents"
identical(
x = SeuratObject::FetchData(
object = sobj,
vars = paste0("RNA_snn_res.", l_res))[[1]],
y = unname(Seurat::Idents(object = sobj))
)
Show output
[1] TRUE
## DimPlot without specifying the resolution
Seurat::DimPlot(
object = sobj,
reduction = "umap",
label = TRUE,
repel = TRUE)
Show plot

Save the Seurat object
We will save our Seurat object that now contains our clustering results :
# saverds2
## 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
# rsession
utils::sessionInfo()
Show output
R version 4.4.1 (2024-06-14)
Platform: x86_64-conda-linux-gnu
Running under: Ubuntu 22.04.5 LTS
Matrix products: default
BLAS/LAPACK: /shared/ifbstor1/software/miniconda/envs/r-4.4.1/lib/libopenblasp-r0.3.29.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] ggraph_2.2.1 ggplot2_3.5.2 future_1.49.0
loaded via a namespace (and not attached):
[1] RColorBrewer_1.1-3 rstudioapi_0.17.1 jsonlite_2.0.0
[4] magrittr_2.0.3 ggbeeswarm_0.7.2 spatstat.utils_3.1-4
[7] farver_2.1.2 rmarkdown_2.29 vctrs_0.6.5
[10] ROCR_1.0-11 memoise_2.0.1 spatstat.explore_3.4-3
[13] htmltools_0.5.8.1 sass_0.4.10 sctransform_0.4.2
[16] parallelly_1.45.0 KernSmooth_2.23-24 bslib_0.9.0
[19] htmlwidgets_1.6.4 ica_1.0-3 plyr_1.8.9
[22] plotly_4.10.4 zoo_1.8-14 cachem_1.1.0
[25] igraph_2.1.4 mime_0.13 lifecycle_1.0.4
[28] pkgconfig_2.0.3 Matrix_1.7-3 R6_2.6.1
[31] fastmap_1.2.0 fitdistrplus_1.2-2 shiny_1.10.0
[34] digest_0.6.37 patchwork_1.3.0 Seurat_5.3.0
[37] tensor_1.5 RSpectra_0.16-2 irlba_2.3.5.1
[40] labeling_0.4.3 progressr_0.15.1 spatstat.sparse_3.1-0
[43] httr_1.4.7 polyclip_1.10-7 abind_1.4-8
[46] compiler_4.4.1 withr_3.0.2 backports_1.5.0
[49] viridis_0.6.5 fastDummies_1.7.5 ggforce_0.4.2
[52] MASS_7.3-65 tools_4.4.1 vipor_0.4.7
[55] lmtest_0.9-40 beeswarm_0.4.0 httpuv_1.6.15
[58] future.apply_1.11.3 goftest_1.2-3 glue_1.8.0
[61] nlme_3.1-165 promises_1.3.2 grid_4.4.1
[64] checkmate_2.3.2 Rtsne_0.17 cluster_2.1.6
[67] reshape2_1.4.4 generics_0.1.4 gtable_0.3.6
[70] spatstat.data_3.1-6 rmdformats_1.0.4 tidyr_1.3.1
[73] data.table_1.17.4 tidygraph_1.3.1 sp_2.2-0
[76] spatstat.geom_3.4-1 RcppAnnoy_0.0.22 ggrepel_0.9.6
[79] RANN_2.6.2 pillar_1.10.2 stringr_1.5.1
[82] limma_3.60.6 spam_2.11-1 RcppHNSW_0.6.0
[85] later_1.4.2 splines_4.4.1 dplyr_1.1.4
[88] tweenr_2.0.3 lattice_0.22-6 survival_3.7-0
[91] deldir_2.0-4 tidyselect_1.2.1 miniUI_0.1.2
[94] pbapply_1.7-2 knitr_1.50 gridExtra_2.3
[97] bookdown_0.39 scattermore_1.2 xfun_0.52
[100] graphlayouts_1.1.1 statmod_1.5.0 matrixStats_1.5.0
[103] stringi_1.8.7 lazyeval_0.2.2 yaml_2.3.10
[106] evaluate_1.0.3 codetools_0.2-20 tibble_3.2.1
[109] cli_3.6.5 uwot_0.2.3 xtable_1.8-4
[112] reticulate_1.42.0 jquerylib_0.1.4 dichromat_2.0-0.1
[115] Rcpp_1.0.14 globals_0.18.0 spatstat.random_3.4-1
[118] png_0.1-8 ggrastr_1.0.2 spatstat.univar_3.1-3
[121] parallel_4.4.1 presto_1.0.0 dotCall64_1.2
[124] listenv_0.9.1 viridisLite_0.4.2 scales_1.4.0
[127] ggridges_0.5.6 SeuratObject_5.1.0 purrr_1.0.4
[130] rlang_1.1.6 cowplot_1.1.3
LS0tCnRpdGxlOiAiPENFTlRFUj5FQjNJIG4xIDIwMjUgc2NSTkFzZXE8QlI+LTxCUj48Qj5QUk9DRVNTSU5HIChJSSk8L0I+PEJSPi08QlI+RGltZW5zaW9uIHJlZHVjdGlvbiAmIHZpc3VhbGl6YXRpb248L0NFTlRFUj4iCmRhdGU6ICIyMDI1LTE2LTIxLjIyIgphdXRob3I6CiAgLSBuYW1lOiAiRUIzSSBuMSBzY1JOQXNlcSBUZWFtIgogIC0gbmFtZTogIkJhc3RpZW4gSk9CIgogICAgZW1haWw6ICJiYXN0aWVuLmpvYkBndXN0YXZlcm91c3N5LmZyIgogIC0gbmFtZTogIkxpbGlhIFlPVU5TSSIKICAgIGVtYWlsOiAibGlsaWEueW91bnNpQGluc2VybS5mciIgCm91dHB1dDoKICBybWRmb3JtYXRzOjpyZWFkdGhlZG93bjoKICAgIGZpZ193aWR0aDogOAogICAgZmlnX2hlaWdodDogNgogICAgaGlnaGxpZ2h0OiB0YW5nbyAgIyMgVGhlbWUgZm9yIHRoZSBjb2RlIGNodW5rcwogICAgZW1iZWRfZm9udHM6IFRSVUUKICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZSAgIyMgQWRkcyBudW1iZXIgdG8gaGVhZGVycyAoc2VjdGlvbnMpCiAgICB0aGVtZTogZmxhdGx5ICAjIyBDU1MgdGhlbWUgZm9yIHRoZSBIVE1MIHBhZ2UKICAgIGNvbGxhcHNlZDogdHJ1ZSAgIyMgQnkgZGVmYXVsdCwgdGhlIFRPQyBpcyBmb2xkZWQKICAgIHRvY19kZXB0aDogMwogICAgc21vb3RoX3Njcm9sbDogdHJ1ZSAjIyBTbW9vdGggc2Nyb2xsIG9mIHRoZSBIVE1MIHBhZ2UKICAgIHNlbGZfY29udGFpbmVkOiB0cnVlICMjIEluY2x1ZGVzIGFsbCBwbG90cy9pbWFnZXMgd2l0aGluIHRoZSBIVE1MCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlICMjIEFkZHMgYSBidXR0b24gdG8gZG93bmxvYWQgdGhlIFJtZAogICAgY29kZV9mb2xkaW5nOiBzaG93CiAgICB0aHVtYm5haWxzOiBmYWxzZQogICAgbGlnaHRib3g6IHRydWUKICAgIGZpZ19jYXB0aW9uOiBmYWxzZQogICAgZ2FsbGVyeTogdHJ1ZQogICAgdXNlX2Jvb2tkb3duOiB0cnVlCmFsd2F5c19hbGxvd19odG1sOiB0cnVlICMjIEFsbG93IHBsYWluIEhUTUwgY29kZSBpbiB0aGUgUm1kCmVkaXRvcl9vcHRpb25zOiAKICBtYXJrZG93bjogCiAgICB3cmFwOiA3MgotLS0KCjwhLS0ga25pdCBzZXR1cCAtLT4KCmBgYHtyIGtuaXRfc2V0dXAsIGVjaG8gPSBGQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KAogIGVjaG8gPSBUUlVFLCAgICAgICAgIyBQcmludCB0aGUgY29kZQogIGV2YWwgPSBUUlVFLCAgICAgICAgIyBSdW4gY29tbWFuZCBsaW5lcwogIG1lc3NhZ2UgPSBGQUxTRSwgICAgIyBQcmludCBtZXNzYWdlcwogIHByb21wdCA9IEZBTFNFLCAgICAgIyBEbyBub3QgZGlzcGxheSBwcm9tcHQKICBjb21tZW50ID0gTkEsICAgICAgICMgTm8gY29tbWVudHMgb24gdGhpcyBzZWN0aW9uCiAgd2FybmluZyA9IEZBTFNFLCAgICAjIERpc3BsYXkgd2FybmluZ3MKICB0aWR5ID0gRkFMU0UsCiAgZmlnLmFsaWduPSJjZW50ZXIiLCAKICAjIHJlc3VsdHMgPSAnaGlkZScsCiAgd2lkdGggPSAxMDAgICAgICAgIyBOdW1iZXIgb2YgY2hhcmFjdGVycyBwZXIgbGluZQopCmBgYAoKPCEtLSBDU1MgdG8gY29sb3IgY2h1bmtzIGFuZCBvdXRwdXRzIC0tPgoKYGBge2NzcywgZWNobz1GQUxTRX0KLm5vdHJ1biB7CiAgYmFja2dyb3VuZC1jb2xvcjogbGlnaHRncmV5ICFpbXBvcnRhbnQ7CiAgYm9yZGVyOiAzcHggc29saWQgYmxhY2sgIWltcG9ydGFudDsKfQoubm90cnVubyB7CiAgYmFja2dyb3VuZC1jb2xvcjogbGlnaHRncmV5ICFpbXBvcnRhbnQ7CiAgY29sb3IgOiBibGFjayAhaW1wb3J0YW50Owp9Ci5xdWVzdGlvbiB7CiAgYmFja2dyb3VuZC1jb2xvcjogYXF1YW1hcmluZSAhaW1wb3J0YW50OwogIGNvbG9yIDogYmxhY2sgIWltcG9ydGFudDsKICBib3JkZXI6IDNweCBzb2xpZCBsaW1lZ3JlZW4gIWltcG9ydGFudDsKfQoucXVlc3Rpb25vIHsKICBiYWNrZ3JvdW5kLWNvbG9yOiBhcXVhbWFyaW5lICFpbXBvcnRhbnQ7CiAgY29sb3IgOiBibGFjayAhaW1wb3J0YW50Owp9Ci5hbnN3ZXIgewogIGJhY2tncm91bmQtY29sb3I6IG5hdmFqb3doaXRlICFpbXBvcnRhbnQ7CiAgYm9yZGVyOiAzcHggc29saWQgYnJvd24gIWltcG9ydGFudDsKfQouYW5zd2VybyB7CiAgYmFja2dyb3VuZC1jb2xvcjogbmF2YWpvd2hpdGUgIWltcG9ydGFudDsKICBjb2xvciA6IGJsYWNrICFpbXBvcnRhbnQ7Cn0KLmJleW9uZCB7CiAgYmFja2dyb3VuZC1jb2xvcjogdmlvbGV0ICFpbXBvcnRhbnQ7CiAgYm9yZGVyOiAzcHggc29saWQgcHVycGxlICFpbXBvcnRhbnQ7Cn0KLmJleW9uZG8gewogIGJhY2tncm91bmQtY29sb3I6IHZpb2xldCAhaW1wb3J0YW50OwogIGNvbG9yIDogYmxhY2sgIWltcG9ydGFudDsKfQpgYGAKCjwhLS0gSG9vayB0byBoYW5kbGUgY29kZSBibG9ja3Mgb3V0cHV0IGZvbGRpbmcgLS0+CgpgYGB7ciBrbml0X2hvb2ssIGVjaG8gPSBGQUxTRX0KaG9va3MgPSBrbml0cjo6a25pdF9ob29rcyRnZXQoKQpob29rX2ZvbGRhYmxlID0gZnVuY3Rpb24odHlwZSkgewogIGZvcmNlKHR5cGUpCiAgZnVuY3Rpb24oeCwgb3B0aW9ucykgewogICAgcmVzID0gaG9va3NbW3R5cGVdXSh4LCBvcHRpb25zKQogICAgCiAgICBpZiAoaXNGQUxTRShvcHRpb25zW1twYXN0ZTAoImZvbGQuIiwgdHlwZSldXSkpIHJldHVybihyZXMpCiAgICAKICAgIHBhc3RlMCgKICAgICAgIjxkZXRhaWxzPjxzdW1tYXJ5PlNob3cgIiwgdHlwZSwgIjwvc3VtbWFyeT5cblxuIiwKICAgICAgcmVzLAogICAgICAiXG5cbjwvZGV0YWlscz4iCiAgICApCiAgfQp9CmtuaXRyOjprbml0X2hvb2tzJHNldCgKICBvdXRwdXQgPSBob29rX2ZvbGRhYmxlKCJvdXRwdXQiKSwKICBwbG90ID0gaG9va19mb2xkYWJsZSgicGxvdCIpCikKYGBgCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKPGNlbnRlcj4hW10oZWIzaV9iYW5uZXIucG5nKTwvY2VudGVyPgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgUFJFQU1CTEUKCiMjIFB1cnBvc2Ugb2YgdGhpcyBzZXNzaW9uCgpUaGlzIGZpbGUgZGVzY3JpYmVzIHRoZSBkaWZmZXJlbnQgc3RlcHMgdG8gcGVyZm9ybSAqKmZpZnRoKiogcGFydCBvZiB0aGUgZGF0YSBwcm9jZXNzaW5nIGZvciB0aGUgc2luZ2xlIGNlbGwgUk5Bc2VxIGRhdGEgYW5hbHlzaXMgdHJhaW5pbmcgY291cnNlIGZvciB0aGUgKipFQjNJIG4xIDIwMjUqKiwgY292ZXJpbmcgdGhlc2Ugc3RlcHMgOgoKLSAgICoqRGltZW5zaW9uIHJlZHVjdGlvbioqIG9mIHRoZSBleHByZXNzaW9uIGRhdGEKCi0gICAqKlZpc3VhbGl6YXRpb24qKiBvZiBjZWxscyBleHByZXNzaW9uIGluIGEgMi1EIHNwYWNlCgotICAgVW5zdXBlcnZpc2VkICoqY2x1c3RlcmluZyoqIG9mIGNlbGxzCgotICAgKipEZXNjcmlwdGlvbioqIG9mIHRoZSBkZWZpbmVkIGNsdXN0ZXJzCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBTdGFydCBSc3R1ZGlvCgotICAgVXNpbmcgdGhlIFtPcGVuT25EZW1hbmQvUnN0dWRpbyBjaGVhdAogICAgc2hlZXRdKGh0dHBzOi8vbW9vZGxlLmZyYW5jZS1iaW9pbmZvcm1hdGlxdWUuZnIvcGx1Z2luZmlsZS5waHAvMTQ3NS9tb2RfZm9sZGVyL2NvbnRlbnQvMC9Pb0RfUl9Sc3R1ZGlvLmh0bWwpLAogICAgY29ubmVjdCB0byB0aGUgW09wZW5PbkRlbWFuZAogICAgcG9ydGFsXShodHRwczovL29uZGVtYW5kLmNsdXN0ZXIuZnJhbmNlLWJpb2luZm9ybWF0aXF1ZS5mcikgYW5kCiAgICAqKmNyZWF0ZSBhIFJzdHVkaW8gc2Vzc2lvbioqIHdpdGggdGhlIHJpZ2h0IHJlc291cmNlIHJlcXVpcmVtZW50cy4KCiMgV2FybS11cAoKLSAgIFdlIHNldCAqKmNvbW1vbiBwYXJhbWV0ZXJzKiogd2Ugd2lsbCB1c2UgdGhyb3VnaG91dCB0aGlzIHNlc3Npb24gOgoKYGBge3Igc2V0cGFyYW19CiMgc2V0cGFyYW0KCgojIyBTZXQgeW91ciBwcm9qZWN0IG5hbWUKIyBXQVJOSU5HIDogRG8gbm90IGp1c3QgY29weS1wYXN0ZSB0aGlzICEgSXQncyBNWSBwcm9qZWN0IG5hbWUgISBQdXQgWU9VUlMgISEKcHJvamVjdF9uYW1lIDwtICJlYmFpaV9zY190ZWFjaGVycyIKCgojIyBDb250cm9sIGlmIHRoZSBwcm9qZWN0X25hbWUgZXhpc3RzIG9uIHRoZSBjbHVzdGVyCmNhdCgnUEFUSCBDSEVDSyA6ICcsIGRpci5leGlzdHMocGFzdGUwKCcvc2hhcmVkL3Byb2plY3RzLycsIHByb2plY3RfbmFtZSkpKQoKIyMgU2VlZCBmb3IgdGhlIFJORwpteV9zZWVkIDwtIDEzMzdMCgojIyBLbm93biBtYXJrZXIgZ2VuZXMgZm9yIFREM0EKdGQzYV9tYXJrZXJzIDwtIGMoIkFwb2UiLCAiQmlyYzUiLCAiUGxhYzgiLCAiSXRtMmEiLCAiUHRjcmEiLCAiVHJidjI5IiwgIklzZzE1IiwgIkNsZG4xMCIpCgpgYGAKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIFByZXBhcmUgdGhlIGRhdGEgc3RydWN0dXJlCgpXZSB3aWxsIGRvIHRoZSBzYW1lIGFzIGZvciBmb3JtZXIgc3RlcHMsIGp1c3QgY2hhbmdpbmcgdGhlIHNlc3Npb24gbmFtZQo6CgojIyBNYWluIGRpcmVjdG9yeQoKYGBge3IgbWFpbmRpciwgZm9sZC5vdXRwdXQgPSBGQUxTRX0KI21haW5kaXIKCiMjIFByZXBhcmluZyB0aGUgcGF0aApURF9kaXIgPC0gcGFzdGUwKCIvc2hhcmVkL3Byb2plY3RzLyIsIHByb2plY3RfbmFtZSwgIi9TQ19URCIpCgojIyBDcmVhdGluZyB0aGUgcm9vdCBkaXJlY3RvcnkKIyBkaXIuY3JlYXRlKHBhdGggPSBURF9kaXIsIHJlY3Vyc2l2ZSA9IFRSVUUpCgojIyBQcmludCB0aGUgcm9vdCBkaXJlY3Rvcnkgb24tc2NyZWVuCnByaW50KFREX2RpcikKCmBgYAoKIyMgQ3VycmVudCBzZXNzaW9uCgpgYGB7ciBzZXNzaW9uZGlyLCBmb2xkLm91dHB1dCA9IEZBTFNFfQojIHNlc3Npb25kaXIKCiMjIENyZWF0aW5nIHRoZSBzZXNzaW9uIChQcmVwcm9jLjIpIGRpcmVjdG9yeQpzZXNzaW9uX2RpciA8LSBwYXN0ZTAoVERfZGlyLCAiLzA1X1Byb2MuMiIpCmRpci5jcmVhdGUocGF0aCA9IHNlc3Npb25fZGlyLCByZWN1cnNpdmUgPSBUUlVFKQoKIyMgUHJpbnQgdGhlIHNlc3Npb24gZGlyZWN0b3J5IG9uLXNjcmVlbgpwcmludChzZXNzaW9uX2RpcikKCmBgYAoKIyMgSW5wdXQgZGlyZWN0b3J5CgpgYGB7ciBpbmRpciwgZm9sZC5vdXRwdXQgPSBGQUxTRX0KI2luZGlyCgojIyBDcmVhdGluZyB0aGUgSU5QVVQgZGF0YSBkaXJlY3RvcnkKaW5wdXRfZGlyIDwtIHBhc3RlMChzZXNzaW9uX2RpciwgIi9EQVRBIikKZGlyLmNyZWF0ZShwYXRoID0gaW5wdXRfZGlyLCByZWN1cnNpdmUgPSBUUlVFKQoKIyMgUHJpbnQgdGhlIGlucHV0IGRpcmVjdG9yeSBvbi1zY3JlZW4KcHJpbnQoaW5wdXRfZGlyKQoKYGBgCgojIyBPdXRwdXQgZGlyZWN0b3J5CgpgYGB7ciBvdXRkaXIsIGZvbGQub3V0cHV0ID0gRkFMU0V9CiNvdXRkaXIKCiMjIENyZWF0aW5nIHRoZSBPVVRQVVQgZGF0YSBkaXJlY3RvcnkKb3V0cHV0X2RpciA8LSBwYXN0ZTAoc2Vzc2lvbl9kaXIsICIvUkVTVUxUUyIpCmRpci5jcmVhdGUocGF0aCA9IG91dHB1dF9kaXIsIHJlY3Vyc2l2ZSA9IFRSVUUpCgojIyBQcmludCB0aGUgb3V0cHV0IGRpcmVjdG9yeSBvbi1zY3JlZW4KcHJpbnQob3V0cHV0X2RpcikKCmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgUmVsb2FkIHRoZSBTZXVyYXQgT2JqZWN0CgotICAgV2UgY2FuIHJlbG9hZCB0aGUgb2JqZWN0IHdlIHNhdmVkIGF0IHRoZSBmb3JtZXIgc3RlcAoKYGBge3IgZGF0YWxvYWR9CiMjICBkYXRhbG9hZAoKCiMjIFRoaXMgaXMgdGhlIHBhdGggdG8gdGhlIGN1cnJlbnQgRUIzSSBiYWNrdXAKc2Vzc2lvbmlkIDwtICcyNTM4X2ViM2lfbjFfMjAyNScKCgojIyBUaGUgbGF0ZXN0IFNldXJhdCBvYmplY3Qgc2F2ZWQgYXMgUkRTIChuYW1lKQpzb2JqX2ZpbGUgPC0gIjA4X1REM0FfUzVfU2NhbGVkLjJrX1JlZy5QQ3JiXzEyNTA4LjQwMzUuUkRTIgoKIyMgVGhlIGxhdGVzdCBTZXVyYXQgb2JqZWN0IHNhdmVkIGFzIFJEUyAoZnVsbCBwYXRoKQpzb2JqX3BhdGggPC0gcGFzdGUwKFREX2RpciwgCiAgICAgICAgICAgICAgICAgICAgIi8wNF9Qcm9jLjEvUkVTVUxUUy8iLAogICAgICAgICAgICAgICAgICAgIHNvYmpfZmlsZSkKCmZvcmNlIDwtIEZBTFNFICAjIyBUbyBmb3JjZSBhIHJlLWRvd25sb2FkIG9mIGEgWmVub2RvLWhvc3RlZCBiYWNrdXAKbG9jYWwgPC0gRkFMU0UgICMjIFRvIGZvcmNlIGEgbG9hZGluZyBmcm9tIGEgbG9jYWwgYmFja3VwCgojIyBJbiBjYXNlIG9mIGVycm9yL2xvc3QgZGF0YSA6IGZvcmNlIGEgcmVsb2FkIGZyb20gYSBaZW5vZG8gYmFja3VwIHJlcG9zaXRvcnkKaWYoZm9yY2UpIHsKICB6ZW5faWQgPC0gIjE0MDM1MjkzIgogIHplbl9iYWNrdXBfZmlsZSA8LSBwYXN0ZTAoImh0dHBzOi8vemVub2RvLm9yZy9yZWNvcmRzLyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB6ZW5faWQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAiL2ZpbGVzLyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzb2JqX2ZpbGUpCiAgIyMgUmVjcmVhdGUgdGhlIGV4cGVjdGVkIHBhdGggaWYgaXQgZG9lcyBub3QgZXhpc3QKICBkaXIuY3JlYXRlKHBhdGggPSBkaXJuYW1lKHNvYmpfcGF0aCksIHJlY3Vyc2l2ZSA9IFRSVUUpCiAgIyMgRG93bmxvYWQgdGhlIGZpbGUKICBkb3dubG9hZC5maWxlKHVybCA9IHplbl9iYWNrdXBfZmlsZSwKICAgICAgICAgICAgICAgIGRlc3RmaWxlID0gc29ial9wYXRoKQp9CgojIyBJbiBjYXNlIG9mIGVycm9yL2xvc3QgZGF0YSA6IGZvcmNlIGEgcmVsb2FkIGZyb20gYSBsb2NhbCBiYWNrdXAgcmVwb3NpdG9yeQppZihsb2NhbCkgewogIHNvYmpfcGF0aCA8LSBwYXN0ZTAoCiAgICAiL3NoYXJlZC9wcm9qZWN0cy8iLCBzZXNzaW9uaWQsICIvYXRlbGllcl9zY3JuYXNlcS9URC9CQUNLVVAvUkRTLyIsCiAgICBzb2JqX2ZpbGUpCn0KCiMjIExvYWQgdGhlIG9iamVjdApzb2JqIDwtIHJlYWRSRFMoZmlsZSA9IHNvYmpfcGF0aCkKCmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgRGltZW5zaW9uIHJlZHVjdGlvbgoKVGhpcyBzdGVwIG9yaWdpbmF0ZXMgZnJvbSB0aGUgb2JzZXJ2YXRpb24gdGhhdCB3ZSBkbyBub3Qgd2FudCBub3IgbmVlZCB0byBjaGFyYWN0ZXJpemUgKiplYWNoKiogb2Ygb3VyICoqdGhvdXNlbmRzIG9mIGNlbGxzKiosIGJ1dCAqKmdyb3VwcyoqIG9mIHRoZW0gKGNsdXN0ZXJzID8gY2VsbCB0eXBlcyA/IG90aGVyID8pLiBUaHVzLCB3ZSBkbyBubyBuZWVkIGFsbCBkYXRhLCBhbmQgZXZlbiBtYXkgYmVuZWZpdCBmcm9tIHN1Y2ggYSByZWR1Y3Rpb24gOgoKLSAgIFJlZHVjZSB0aGUgZGF0YSBjb21wbGV4aXR5CgogICAgLSAgIEZvciAqKmludGVycHJldGF0aW9uKioKCiAgICAtICAgRm9yICoqY29tcHV0YXRpb25zKioKICAgIAotICAgSW5jcmVhc2UgdGhlIHF1YWxpdHkgb2YgaW5mb3JtYXRpb24gY29udGFpbmVkIGluIHRoZSBkYXRhCgogICAgLSAgICoqRW5yaWNoaW5nKiogImdvb2QiIGJpb2xvZ2ljYWwgKipzaWduYWxzKioKICAgIAogICAgLSAgICoqRGlzY2FyZGluZyBub2lzZSoqIC8gY2VsbC1zcGVjaWZpYyBzaWduYWxzCgpUaGVyZSBpcyBhICoqbXVsdGl0dWRlIG9mIG1ldGhvZHMqKiBmb3IgZGltZW5zaW9uIHJlZHVjdGlvbgo8Y2VudGVyPiFbXShkaW1yZWRfdHJlZS5wbmcpPC9jZW50ZXI+CgoKIyMgUHJpbmNpcGFsIENvbXBvbmVudCBBbmFseXNpcyAoUENBKQoKSGVyZSwgd2Ugd2lsbCB1c2UgdGhlIGdyYW5kLW1vdGhlciBvZiBhbGwgOiB0aGUgUENBIChQcmluY2lwYWwgQ29tcG9uZW50IEFuYWx5c2lzKQoKYGBge3IgaF9SdW5QQ0EsIGV2YWwgPSBGQUxTRX0KIyBoX3J1blBDQQoKP1NldXJhdDo6UnVuUENBKCkKCmBgYAoKKipRdWVzdGlvbnMgOiDirY3irY0gTGlnaHRuaW5nIHF1aXp6IOKtjeKtjSAqKiA6IAoKYGBge3IgcV9wY2ExLCBjbGFzcy5zb3VyY2U9InF1ZXN0aW9uIiwgZXZhbCA9IEZBTFNFfQojIHFfcGNhMQoKSG93IG1hbnkgcHJpbmNpcGFsIGNvbXBvbmVudHMgKFBDKSB3aWxsIGJlIGdlbmVyYXRlZCBieSBkZWZhdWx0ID8KCmBgYAoKPGJyPgoKYGBge3IgYV9wY2ExLCBjbGFzcy5zb3VyY2UgPSBjKCJmb2xkLWhpZGUiLCAiYW5zd2VyIiksIGV2YWwgPSBGQUxTRX0KIyBhX3BjYTEKCiMjIC4gVGhlIGFuc3dlciBpcyA1MCAobnBjcyBwYXJhbWV0ZXIpCiMjCiMjIC4gV2Ugd2lsbCB1c2UgdGhpcyBkZWZhdWx0IHZhbHVlLgojIwojIyAuIFdhcm5pbmcgOiBpbiBzb21lIChyYXJlKSBjb250ZXh0cywgdGhpcwojIyAgIG1heSBub3QgYmUgZW5vdWdoICEKCmBgYAoKPGJyPgoKYGBge3IgcV9wY2EyLCBjbGFzcy5zb3VyY2U9InF1ZXN0aW9uIiwgZXZhbCA9IEZBTFNFfQojIHFfcGNhMgoKV2hpY2ggZGF0YSB0eXBlIChpZSwgd2hpY2ggU2V1cmF0IG9iamVjdCBsYXllcikgd2lsbCBiZSB1c2VkIAp0byBnZW5lcmF0ZSB0aGUgY29tcG9uZW50cyA/CgpgYGAKCjxicj4KCmBgYHtyIGFfcGNhMiwgY2xhc3Muc291cmNlID0gYygiZm9sZC1oaWRlIiwgImFuc3dlciIpLCBldmFsID0gRkFMU0V9CiMgYV9wY2EyCgojIyAuIERhdGEgZnJvbSB0aGUgc2NhbGUuZGF0YSBsYXllciB3aWxsIGJlIHVzZWQKIyMKIyMgLiBUaGlzIGlzIHVuZm9ydHVuYXRlbHkgbm90IHJlYWxseSBleHBsaWNpdAojIyAgIGZyb20gdGhlIFNldXJhdDo6UnVuUENBIGhlbHAgcGFnZSAhIChzZWUgdGhlICJmZWF0dXJlcyIKIyMgICBwYXJ0KQojIwojIyAuIFdlYiBzZWFyY2ggOiAid2hpY2ggc2xvdCBpcyB1c2VkIGJ5IFJ1blBDQSIKIyMiCiMjIC4gV2hlbiBydW4gb24gYSBTZXVyYXQgb2JqZWN0IHdpdGhvdXQgQHNjYWxlLmRhdGEgbGF5ZXIgOgojIyAgICJFcnJvciBpbiBHZXRBc3NheURhdGEob2JqZWN0LCBhc3NheS50eXBlID0gYXNzYXkudHlwZSwgc2xvdCA9ICJzY2FsZS5kYXRhIikgOiAKIyMgICBPYmplY3RAc2NhbGUuZGF0YSBoYXMgbm90IGJlZW4gc2V0LiBSdW4gU2NhbGVEYXRhKCkgYW5kIHRoZW4gcmV0cnkuIgoKYGBgCgo8YnI+CgpQZXJmb3JtIFBDQSBvbiBvdXIgZGF0YQoKYGBge3IgcGNhfQojIHBjYQoKIyMgTm90ZSA6IGEgc2VlZCBpcyB1c2VkIGhlcmUgIQpzb2JqIDwtIFNldXJhdDo6UnVuUENBKAogIG9iamVjdCA9IHNvYmosIAogIGFzc2F5ID0gJ1JOQScsIAogIHNlZWQudXNlID0gbXlfc2VlZCwgCiAgdmVyYm9zZSA9IEZBTFNFKQoKYGBgCgo8YnI+CgpEZXNjcmlwdGlvbiA6CgpgYGB7ciBQQ0FkZXNjLCBjbGFzcy5zb3VyY2U9Im5vdHJ1biIsIGNsYXNzLm91dHB1dD0ibm90cnVubyIsIGV2YWwgPSBGQUxTRX0KIyBQQ0FkZXNjCgojIyBQbGVhc2UsIGZvY3VzIG9uIHRoZSAicmVkdWN0aW9ucyIgc2xvdApWaWV3KHNvYmopCgpgYGAKCjxicj4KClZpc3VhbGl6YXRpb24gb2YgdGhlIHZlcnkgZmlyc3QgKip0d28gY29tcG9uZW50cyoqLCB3aXRoIGNlbGxzIGNvbG9yaW5nIGFjY29yZGluZyB0byB0aGUgZXN0aW1hdGVkICoqY2VsbCBjeWNsZSBwaGFzZSoqIDoKCmBgYHtyIFBDQXBsb3R9CiMgUENBcGxvdAoKIyMgU2NhdHRlciBwbG90IGFsb25nIGRpbWVuc2lvbnMKU2V1cmF0OjpEaW1QbG90KAogIG9iamVjdCA9IHNvYmosIAogICMjIEZpcnN0IHR3byBjb21wb25lbnRzCiAgZGltcyA9IGMoMSwyKSwgCiAgIyMgQ29sb3IgZG90cyBwZXIgY2VsbCBwaGFzZSBncm91cHMKICBncm91cC5ieSA9ICdDQ19TZXVyYXRfUGhhc2UnLCAKICAjIyBEYXRhIHRvIHVzZQogIHJlZHVjdGlvbiA9ICdwY2EnKQoKYGBgCgo8YnI+CgojIyBRdWVzdGlvbnMKCmBgYHtyIHFfcGNhMywgY2xhc3Muc291cmNlPSJxdWVzdGlvbiIsIGV2YWwgPSBGQUxTRX0KIyBxX3BjYTMKCkdpdmUgdXMgeW91ciBpbnRlcnByZXRhdGlvbiAvIGZlZWxpbmdzIGZyb20gdGhpcyBwbG90ICEKCmBgYAoKPGJyPgoKYGBge3IgcV9wY2E0LCBjbGFzcy5zb3VyY2U9InF1ZXN0aW9uIiwgZXZhbCA9IEZBTFNFfQojIHFfcGNhNAoKU2hvdWxkIHdlIGxpbWl0IG91cnNlbHZlcyB0byB1c2luZyAyIGRpbWVuc2lvbnMgdG8gaW50ZXJwcmV0IG91ciBkYXRhID8KCmBgYAoKPGJyPjxicj48YnI+CgotICAgTWF5YmUgd2Ugc2hvdWQgKipyZWR1Y2UqKiBpbmZvcm1hdGlvbiBhIHRhZCAqKm1vcmUqKiwganVzdCBmb3IgdGhlIHNha2Ugb2YgLi4uIAoKICAgIC0gICAuLi4gdW5kZXJzdGFuZGluZyBvdXIgZGF0YSAuLi4KCiAgICAtICAgLi4ud2l0aCBvdXIgKipwb29yIGh1bWFuIGJyYWlucyoqIC4uLgoKICAgIC0gICAuLi4gYm9ybiBhbmQgcmFpc2VkIGluIGEgM0QgKipldWNsaWRlYW4gd29ybGQqKiAhCgo8IS0tIEFTQ0lJIEFSVCBHRU5FUkFUT1IgSSdNIFdJVEggU1RPT1BJRCAtLT4KCmBgYHtyIHN0b29waWQsIGNsYXNzLnNvdXJjZSA9IGMoImZvbGQtaGlkZSIsICJub3RydW4iKX0KIyMKIyMgICAgIF9fICAgICAgICAgICAgICAuX19fL1wgICAgICAgICAgICAgICAgLl9fICBfXyAgLl9fICAgICAgICAgICAgICAgIF9fICAgICAgICAgICAgICAgLl9fICAgIC5fX18gLl8uCiMjICAgIC8gLyAgIF9fX19fXyAgICAgfCAgICkvX19fX18gICBfXyAgXyAgX3xfX3wvICB8X3wgIHxfXyAgICAgX19fX19fXy8gIHxfIF9fIF9fX19fX19fIHxfX3wgX198IF8vIHwgfAojIyAgIC8gLyAgIC9fX19fXy8gICAgIHwgICB8LyAgICAgXCAgXCBcLyBcLyAvICBcICAgX19cICB8ICBcICAgLyAgX19fL1wgICBfX1wgIHwgIFxfX19fIFx8ICB8LyBfXyB8ICB8IHwKIyMgICBcIFwgICAvX19fX18vICAgICB8ICAgfCAgWSBZICBcICBcICAgICAvfCAgfHwgIHwgfCAgIFkgIFwgIFxfX18gIFwgfCAgfCB8ICB8ICAvICB8Xz4gPiAgLyAvXy8gfCAgIFx8CiMjICAgIFxfXCAgICAgICAgICAgICAgfF9fX3xfX3xffCAgLyAgIFwvXF8vIHxfX3x8X198IHxfX198ICAvIC9fX19fICAvIHxfX3wgfF9fX18vfCAgIF9fL3xfX1xfX19fIHwgICBfXwojIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBcLyAgICAgICAgICAgICAgICAgICAgICAgIFwvICAgICAgIFwvICAgICAgICAgICAgIHxfX3wgICAgICAgICAgIFwvICAgXC8KIyMKYGBgCgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgVmlzdWFsaXphdGlvbgoKVGhpcyBmaW5hbCBwcm9jZXNzaW5nIHN0ZXAgbmVlZCB0byBmaW5hbGx5ICoqb2JzZXJ2ZSoqIG91ciBkYXRhIHJlcXVpcmVzIGEgbm92ZWwgZGltZW5zaW9uIHJlZHVjdGlvbiBtZXRob2Qgd2l0aCBhIHZlcnkgaGlnaCBjaGFsbGVuZ2UgdG8gb3ZlcmNvbWUgOiByZWR1Y2UgYSBzcGFjZSBvZiBkb3plbnMgb2YgZGltZW5zaW9ucyB0byAqKmp1c3QgYSBmZXcqKiAhCgpXZSB3aWxsIHVzZSB0aGUgWyoqVU1BUCoqXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9VTUFQKXt0YXJnZXQ9Il9ibGFuayJ9IG1ldGhvZC4KCiMjIFVuaWZvcm0gTWFuaWZvbGQgQXBwcm94aW1hdGlvbiBhbmQgUHJvamVjdGlvbiAoVU1BUCkKCkhvdyA/CgpgYGB7ciBodW1hcCwgY2xhc3Muc291cmNlPSJub3RydW4iLCBjbGFzcy5vdXRwdXQ9Im5vdHJ1bm8iLCBldmFsID0gRkFMU0V9CiMgaHVtYXAKCj9TZXVyYXQ6OlJ1blVNQVAoKQoKYGBgCgojIyMgU2VsZWN0IGRpbWVuc2lvbnMKCldlIGdlbmVyYXRlZCAqKjUwIFBDQSBjb21wb25lbnRzKiogZnJvbSBvdXIgfjEyIEsgZmVhdHVyZXMKCi0gICBUaGVzZSA1MCBkaW1lbnNpb25zIG1heSAqKm5vdCBhbGwqKiBjb250YWluICoqdmFsdWFibGUqKiBpbmZvcm1hdGlvbgoKLSAgIFdlIHNob3VsZCB0cnkgZG8gKipzZWxlY3QgdGhlIG1vc3QgdXNlZnVsKiogb25lcyBhbmQgKipkaXNjYXJkKiogdGhlIHJlbWFpbmluZyAqKm5vaXNlKioKCi0gICBCdXQgKipob3cgbWFueSoqIHNob3VsZCB3ZSBrZWVwID8KCi0gICAqKlF1ZXN0aW9uKiogOiAKCiAgICBgYGB7ciBxX25kaW0xLCBjbGFzcy5zb3VyY2U9InF1ZXN0aW9uIiwgZXZhbCA9IEZBTFNFfQogICAgIyBxX25kaW0xCiAgICAKICAgIERvIHlvdSBoYXZlIGFuIGlkZWEgYWJvdXQgdGhpcyBudW1iZXIgPwogICAgCiAgICBgYGAKICAgIAogICAgPGJyPgoKICAgIGBgYHtyIHJfbmRpbTEsIGNsYXNzLnNvdXJjZSA9IGMoImZvbGQtaGlkZSIsICJhbnN3ZXIiKSwgZXZhbCA9IEZBTFNFfQogICAgIyByX25kaW0xCiAgICAKICAgICMjIC4gSW1wb3NzaWJsZSB0byBndWVzcyB3aXRoIG91ciBjdXJyZW50IGtub3dsZWRnZS4KICAgICMjCiAgICAjIyAuIEJ1dCB3ZSBjYW4gZ2V0IHNvbWUgaGVscCBmcm9tIHRoZSBQQ0EgZGF0YSBpdHNlbGYKICAgICMjCiAgICAjIyAuIElmIHlvdSBzYWlkIGEgdmFsdWUgYWJvdmUgdGhlIDUwIGNvbXBvbmVudHMgd2UKICAgICMjICAgZ2VuZXJhdGVkIGZvciBvdXIgUENBLCB5b3Ugc2hvdWxkIHdlYXIgdGhlCiAgICAjIyAgIGNvbmUgb2Ygc2hhbWUgIQogICAgCiAgICBgYGAKICAgIAogICAgPGJyPgoKVGhlcmUgYXJlICoqc2V2ZXJhbCoqIG1ldGhvZHMgdG8gaGVscCB1cyBjaG9vc2UuCgpTZXZlcmFsLCBidXQgKipub25lIHBlcmZlY3QqKi4KCldlIHdpbGwgdXNlIGEgdmVyeSAqKnNpbXBsZSwgZ3JhcGhpY2FsKiogbWV0aG9kIDogdGhlIG9ic2VydmF0aW9uIG9mIHRoZSBhbW91bnQgb2YgZ2xvYmFsIHZhcmlhbmNlIGV4cGxhaW5lZCBieSBlYWNoIGNvbXBvbmVudCwgdGhyb3VnaCB0aGUgYGVsYm93LXBsb3RgLgoKPGJyPgo8Y2VudGVyPiFbXShlbGJvd19rbmVlX0VMQk9XLnBuZyk8L2NlbnRlcj4KPGJyPgoKYGBge3IgaF9lbGJvdywgY2xhc3Muc291cmNlPSJub3RydW4iLCBjbGFzcy5vdXRwdXQ9Im5vdHJ1bm8iLCBldmFsID0gRkFMU0V9CiMgaF9lbGJvdwoKP1NldXJhdDo6RWxib3dQbG90KCkKCmBgYAoKPGJyPgoKQXBwbHkgb24gb3VyIGRhdGEgOgoKYGBge3IgZWxib3d9CiMgZWxib3cKCiMjIFBlcmZvcm0gdGhlICJlbGJvdyBwbG90IgpTZXVyYXQ6OkVsYm93UGxvdCgKICBvYmplY3QgPSBzb2JqLCAKICByZWR1Y3Rpb24gPSAncGNhJywKICBuZGltcyA9IDUwKQoKYGBgCgo8YnI+CgoqKlF1ZXN0aW9uKiogOiAKCmBgYHtyIHFfbmRpbTIsIGNsYXNzLnNvdXJjZT0icXVlc3Rpb24iLCBldmFsID0gRkFMU0V9CiMgcV9uZGltMgoKQW55IG1vcmUgcHJlY2lzZSBpZGVhLCBub3cgPwoKYGBgCgo8YnI+CgpgYGB7ciByX25kaW0yLCBjbGFzcy5zb3VyY2UgPSBjKCJmb2xkLWhpZGUiLCAiYW5zd2VyIiksIGV2YWwgPSBGQUxTRX0KIyByX25kaW0yCgojIyAuIFRoZSBjb250cmlidXRpb24gdG8gdGhlIHZhcmlhbmNlIChzZMKyKSBzZWVtcwojIyAgIGdyZWF0bHkgcmVkdWNlZCBhZnRlciAzMCBQQ3MuCiMjCiMjIC4gTWF5YmUgc29tZXRoaW5nIGJldHdlZW4gfjE1IGFuZCB+MzAgc2hvdWxkIGRvCiMjICAgdGhlIHRyaWNrID8KYGBgCgo8YnI+CgojIyMgQXNzZXNzIGRpbWVuc2lvbnMKClRvIGRlbW9uc3RyYXRlIHRoZSAqKmVmZmVjdCoqIG9mIHRoZSBudW1iZXIgb2YgUEMgZGltZW5zaW9ucyB1c2VkIGFzIGlucHV0IHRvIHRoZSBVTUFQIGdlbmVyYXRpb24sIHdlIHdpbGwgcGVyZm9ybSBhIGNvbXBhcmlzb24gdXNpbmcgYDZgIGRpZmZlcmVudCBhbW91bnRzIG9mIHJldGFpbmVkIFBDcyA6Cgo8YnI+CjxDRU5URVI+KipUZUFtV29SayBUaU1lICEqKgo8YnI+CiFbXShhMm1pYmxldWZyYWlzZTUwLmpwZykKPC9DRU5URVI+Cjxicj4KCldlIHdpbGwgZGlzcGF0Y2ggdGhlIGFzc2Vzc21lbnQgb2YgZWFjaCBhbW91bnQgb2YgZGltZW5zaW9ucyB0byBncm91cHMgCm9mIHRyYWluZWVzLgoKYGBge3IgZGltX3NlbCwgZmlnLndpZHRoID0gMjQsIGZpZy5oZWlnaHQgPSAxMn0KIyBkaW1fc2VsCgojIyBQQ0EgbWF4IGRpbWVuc2lvbnMgdG8gZXZhbHVhdGUKcGNhX2RpbXMgPC0gYygzLCA3LCAxNywgMjUsIDMwLCA0MCkKCiMjIERlZmluZSBhIGZ1bmN0aW9uIHRvIGNvbXB1dGUgdGhlIFVNQVAKcGNhX2RpbV9ldmFsIDwtIGZ1bmN0aW9uKG9iamVjdCA9IE5VTEwsIGRpbS5tYXggPSAyLCBteV9zZWVkID0gMTMzN0wpIHsKICAKICBtZXNzYWdlKCdcblJ1bm5pbmcgVU1BUCB3aXRoICcsIGRpbS5tYXgsICcgZGltZW5zaW9ucyAuLi5cbicpCiAgCiAgIyMgUnVuVU1BUAogIG9iamVjdCA8LSBTZXVyYXQ6OlJ1blVNQVAoCiAgICBvYmplY3QgPSBvYmplY3QsIGFzc2F5ID0gIlJOQSIsIAogICAgcmVkdWN0aW9uID0gInBjYSIsIGRpbXMgPSAxOmRpbS5tYXgsIAogICAgc2VlZC51c2UgPSBteV9zZWVkKQogIAogICMjIFBsb3QKICBkcE4gPC0gU2V1cmF0OjpEaW1QbG90KAogICAgb2JqZWN0ID0gb2JqZWN0LCAKICAgIHJlZHVjdGlvbiA9ICd1bWFwJywKICAgIGNvbWJpbmUgPSBUUlVFKSArIGdncGxvdDI6OmdndGl0bGUoCiAgICAgIGxhYmVsID0gcGFzdGUwKCJEaW0gOiAiLCBkaW0ubWF4KSkgKyBTZXVyYXQ6OkRhcmtUaGVtZSgpCiAgCiAgIyMgQ2xlYW4KICBybShvYmplY3QpCiAgCiAgIyMgUmV0dXJuIHRoZSBwbG90IG9iamVjdAogIHJldHVybihkcE4pCn0KCiMjIFJ1biB0aGUgZnVuY3Rpb24gb24gbXVsdGlwbGUgZGltZW5zaW9ucywgZ2V0IGEgbGlzdCBvZiBnZ3Bsb3RzCnBjYV9ldmFsX3JlcyA8LSBsYXBwbHkoWCA9IHBjYV9kaW1zLAogICAgICAgICAgICAgICAgICAgICAgIEZVTiA9IGZ1bmN0aW9uKHApIHsKICAgICAgICAgICAgICAgICAgICAgICAgIHBjYV9kaW1fZXZhbChvYmplY3QgPSBzb2JqLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaW0ubWF4ID0gcCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBteV9zZWVkID0gbXlfc2VlZCkKICAgICAgICAgICAgICAgICAgICAgICB9KQoKIyMgUGxvdCB0aGUgbGlzdCBhbGx0b2dldGhlcgpwYXRjaHdvcms6OndyYXBfcGxvdHMocGNhX2V2YWxfcmVzLCBucm93ID0gMikKCmBgYAoKPGJyPgoKKipRdWVzdGlvbioqCgpgYGB7ciBxX25kaW0zLCBjbGFzcy5zb3VyY2U9InF1ZXN0aW9uIiwgZXZhbCA9IEZBTFNFfQojIHFfbmRpbTMKCkFueSBtb3JlIHByZWNpc2UgaWRlYSwgbm93LCBGT1IgUkVBTCA/CgpgYGAKCjxicj4KCmBgYHtyIHJfbmRpbTMsIGNsYXNzLnNvdXJjZSA9IGMoImZvbGQtaGlkZSIsICJhbnN3ZXIiKSwgZXZhbCA9IEZBTFNFfQojIHJfbmRpbTMKCiMjIC4gQSBsaW1pdGVkIGFtb3VudCBvZiBQQ3MgaXMgbm90IGFibGUgdG8gY2FwdHVyZQojIyAgIGVub3VnaCBpbmZvcm1hdGlvbiAodmFyaWF0aW9uKSB0byBidWlsZCBhIHN1ZmZpY2llbnRseSAKIyMgICBkZWZpbmVkIHRvcG9sb2d5LgojIwojIyAuIFRoZSBkaWZmZXJlbmNlcyBpbiB0aGUgZ2xvYmFsIHRvcG9sb2d5IGlzCiMjICAgaXMgcXVpdGUgbGltaXRlZCBiZXR3ZWVuIHRoZSBoaWdoZXIgUENzIHZlcnNpb25zLgojIwojIyAuIFRoaXMgbWF5IGltcGx5IHRoYXQgdGhlIGFkZGl0aW9uYWwKIyMgICBjb21wb25lbnRzIGFib3ZlIDIwfjI1IGRvIG5vdCBhZGQgbW9yZSAKIyMgICBpbmZvcm1hdGlvbiAobmVpdGhlciBtb3JlIG5vaXNlLCBpbiB0aGlzIGNhc2UpLgoKYGBgCgo8YnI+CgpXZSBjYW4gbm93IHBlcmZvcm0gdGhlIGZpbmFsIFVNQVAgd2l0aCB0aGUgUEMgZGltZW5zaW9ucyBvZiB5b3VyIGNob2ljZS4KCjxicj4KCiMjIyBDcmVhdGUgdGhlIFVNQVAKCkZvciB0aGUgbmV4dCBzdGVwcyBvZiB0aGUgdHJhaW5pbmcsIHdlIHdpbGwgdXNlICoqYDIwYCoqIFBDQSBkaW1lbnNpb25zLgoKYGBge3IgdW1hcDIwfQojIHVtYXAyMAoKIyMgRml4aW5nIG5fZGltCm5fZGltIDwtIDIwCgojIyBVc2luZyAyMCBQQ3MKIyMgQSBzZWVkIGlzIG5lZWRlZCBoZXJlICEKc29iaiA8LSBTZXVyYXQ6OlJ1blVNQVAoCiAgICBvYmplY3QgPSBzb2JqLCBhc3NheSA9ICJSTkEiLCAKICAgIHJlZHVjdGlvbiA9ICJwY2EiLCAKICAgIGRpbXMgPSAxOm5fZGltLCAKICAgIHNlZWQudXNlID0gbXlfc2VlZCkKCiMjIERpbVBsb3QKdW1hcDJkIDwtIFNldXJhdDo6RGltUGxvdCgKICBvYmplY3QgPSBzb2JqLCAKICByZWR1Y3Rpb24gPSAndW1hcCcpICsgU2V1cmF0OjpEYXJrVGhlbWUoKQpwcmludCh1bWFwMmQpCgpgYGAKCjxicj4KCiMjIEJvbnVzIDogM0QgVU1BUCAoREVNTykKCldoaWxlIGJ5IGRlZmF1bHQgU2V1cmF0OjpSdW5VTUFQIHdpbGwgcHJvZHVjZSAyLWRpbWVuc2lvbiByZWR1Y3Rpb25zLCB0aGUgbWV0aG9kIGNhbiBnZW5lcmF0ZSBmdXJ0aGVyIGNvbXBvbmVudHMuCgpEZXNwaXRlIG91ciBsaW1pdGVkIGJyYWluLCB0aGlzIGlzIHNvbWV0aW1lcyBpbnRlcmVzdGluZyBhbmQgdXNlZnVsIHRvIGF0dGVtcHQgYSByZWR1Y3Rpb24gdG8gMyBkaW1lbnNpb25zIGluc3RlYWQgb2YgMi4gVGhpcyBjYW4gYmUgdmVyeSBlZmZlY3RpdmUgd2hlbiBsb29raW5nIGZvciB0cmFqZWN0b3JpZXMuCgpXZSBjYW4gZ2VuZXJhdGUgYSBVTUFQIHdpdGggMyBjb21wb25lbnRzLCBmcm9tIHRoZSAyMCBQQ3MgOgoKYGBge3IgdW1hcDNEMjAsIGNsYXNzLnNvdXJjZT0ibm90cnVuIiwgY2xhc3Mub3V0cHV0PSJub3RydW5vIn0KIyB1bWFwM0QyMAoKIyMgVU1BUCBmcm9tIDIwIFBDcywgMyBjb21wb25lbnRzIHJlcXVlc3RlZApzb2JqX1UzRCA8LSBTZXVyYXQ6OlJ1blVNQVAoCiAgb2JqZWN0ID0gc29iaiwgYXNzYXkgPSAnUk5BJywgCiAgZ3JhcGgubmFtZSA9ICdSTkFfc25uJywgCiAgcmVkdWN0aW9uID0gJ3BjYScsIAogIHJlZHVjdGlvbi5uYW1lID0gJ3VtYXAzZCcsCiAgZGltcyA9IDE6bl9kaW0sIAogIHNlZWQudXNlID0gbXlfc2VlZCwKICBuLmNvbXBvbmVudHMgPSAzKQoKYGBgCgpXZSBjYW4gcGxvdCB0aGUgdHdvIGZpcnN0IFVNQVAgY29tcG9uZW50cyBmcm9tIHRoaXMgM0Qgc3BhY2UgOgoKYGBge3IsIGNsYXNzLnNvdXJjZT0ibm90cnVuIiwgY2xhc3Mub3V0cHV0PSJub3RydW5vIiwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDh9CiMgcGxvdF91bWFwM0QyMAoKIyMgRGltUGxvdCBvZiB0aGUgZmlyc3QgMiBVTUFQIGNvbXBvbmVudHMKdW1hcDNkIDwtIFNldXJhdDo6RGltUGxvdCgKICBvYmplY3QgPSBzb2JqX1UzRCwgCiAgZGltcyA9IGMoMSwyKSwKICByZWR1Y3Rpb24gPSAndW1hcDNkJykgKyBTZXVyYXQ6OkRhcmtUaGVtZSgpCnByaW50KHVtYXAzZCkKCmBgYAoKPGJyPgoKKipRdWVzdGlvbioqIDogCgpgYGB7ciBxX3VtYXAzRCwgY2xhc3Muc291cmNlPSJxdWVzdGlvbiIsIGV2YWwgPSBGQUxTRX0KIyBxX3VtYXAzRAoKSXNuJ3QgdGhlcmUgc29tZXRoaW5nIHN0cmlraW5nID8KCmBgYAoKPGJyPgoKYGBge3IgYV91bWFwM0QsIGNsYXNzLnNvdXJjZSA9IGMoImZvbGQtaGlkZSIsICJhbnN3ZXIiKSwgZXZhbCA9IEZBTFNFfQojIGFfdW1hcDNECgojIyAuIFRoZSBwbG90IGlzIG5vdCB0aGUgc2FtZSBhcyB3aGVuCiMjICAgdXNpbmcgMjUgUENzIHdoZW4gcmVxdWVzdGluZyAzIFVNQVAKIyMgICBjb21wb25lbnRzIGluc3RlYWQgb2YgMiAhCiMjCiMjIC4gVGhlIDIgY29tcG9uZW50cyBvZiBhIDJEIFVNQVAgYXJlIG5vdAojIyAgIHRoZSBzYW1lIGFzIHRoZSB0d28gZmlyc3QgY29tcG9uZW50cwojIyAgIG9mIGEgZGltPjIgVU1BUC4KCmBgYAoKPGJyPgoKYGBge3IgdW1hcFhkLCBjbGFzcy5zb3VyY2U9Im5vdHJ1biIsIGNsYXNzLm91dHB1dD0ibm90cnVubyIsIGZpZy53aWR0aCA9IDE2LCBmaWcuaGVpZ2h0ID0gOCB9CiMgdW1hcFhkCgpwYXRjaHdvcms6OndyYXBfcGxvdHMoCiAgbGlzdCgKICAgIHVtYXAyZCAmIGdncGxvdDI6OmdndGl0bGUobGFiZWwgPSAiVU1BUCAoMkQpIiksCiAgICB1bWFwM2QgJiBnZ3Bsb3QyOjpnZ3RpdGxlKGxhYmVsID0gIlVNQVAgKDNEKSIpKSwgCiAgbnJvdyA9IDEpCgpgYGAKCjxicj4KCkxldCdzIHBlcmZvcm0gYSAzRCByZXByZXNlbnRhdGlvbiBvZiBvdXIgVU1BUCAoUFJFLVJFTkRFUkVEKQoKYGBge3IgdW1hcDNEMjByZ2wsIGNsYXNzLnNvdXJjZT0ibm90cnVuIiwgY2xhc3Mub3V0cHV0PSJub3RydW5vIiwgZXZhbCA9IEZBTFNFfQojIHVtYXAzRDIwcmdsCgojIyAzRCBwbG90IChub3QgcnVuIGFzIGZhaWxpbmcgaW4gaW50ZXJhY3RpdmUgbW9kZSkKcGxvdGx5OjpwbG90X2x5KAogIGRhdGEgPSBhcy5kYXRhLmZyYW1lKFNldXJhdDo6UmVkdWN0aW9ucygKICAgIG9iamVjdCA9IHNvYmpfVTNELCAKICAgIHNsb3QgPSAidW1hcDNkIilAY2VsbC5lbWJlZGRpbmdzKSwKICB4ID0gfnVtYXAzZF8xLCAKICB5ID0gfnVtYXAzZF8yLCAKICB6ID0gfnVtYXAzZF8zLCAKICB0eXBlID0gJ3NjYXR0ZXIzZCcsIAogIG1hcmtlciA9IGxpc3Qoc2l6ZSA9IDIsIHdpZHRoPTIpKQoKIyMgQ2xlYW4Kcm0oc29ial9VM0QpCgpgYGAKCjxicj4KCjxjZW50ZXI+IVtdKHRkM2FfdW1hcDNkLnBuZyk8L2NlbnRlcj4KCjxicj4KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgo8YnI+CgojIFNhdmUgdGhlIFNldXJhdCBvYmplY3QKCldlIHdpbGwgc2F2ZSBvdXIgU2V1cmF0IG9iamVjdCB0aGF0IG5vdyBjb250YWlucyBQQ0EgYW5kIFVNQVAgcmVkdWN0aW9ucyAgOgoKYGBge3Igc2F2ZXJkczEsIGZvbGQub3V0cHV0ID0gRkFMU0V9CiMgc2F2ZXJkczEKCiMjIFNhdmUgb3VyIFNldXJhdCBvYmplY3QgKHJpY2ggbmFtaW5nKQpvdXRfbmFtZSA8LSBwYXN0ZTAoCiAgICAgICAgICBvdXRwdXRfZGlyLCAiLyIsIHBhc3RlKAogICAgICAgICAgICBjKCIwOSIsIFNldXJhdDo6UHJvamVjdChzb2JqKSwgIlM1IiwgCiAgICAgICAgICAgICAgIkRpbVJlZC5QQ0EiLCBwYXN0ZSgKICAgICAgICAgICAgICAgIGRpbShzb2JqKSwgCiAgICAgICAgICAgICAgICBjb2xsYXBzZSA9ICcuJwogICAgICAgICAgICAgICkKICAgICAgICAgICAgKSwgY29sbGFwc2UgPSAiXyIpLAogICAgICAgICAgICAiLlJEUyIpCgojIyBDaGVjawpwcmludChvdXRfbmFtZSkKCiMjIFdyaXRlIG9uIGRpc2sKc2F2ZVJEUyhvYmplY3QgPSBzb2JqLCAKICAgICAgICBmaWxlID0gb3V0X25hbWUpCgpgYGAKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIENsdXN0ZXJpbmcKCldlIGNhbiBub3cgYXR0ZW1wdCB0byBkZXRlcm1pbmUgaG93IGNlbGxzICoqYXJlIG9yZ2FuaXplZCoqIGluIGFuICoqdW5zdXBlcnZpc2VkKiogbWFubmVyIGluIHRoaXMgc3BhY2UKCldlIHdpbGwgdXNlIHRoZSBncmFwaC1iYXNlZCBjbHVzdGVyaW5nIG1ldGhvZCBbTG91dmFpbl0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvTG91dmFpbl9tZXRob2Qpe3RhcmdldD0iX2JsYW5rIn0KCkNsdXN0ZXJpbmcgd2lsbCBiZSBwZXJmb3JtZWQgb24gdGhlICoqYFBDQWAqKiBkaW1lbnNpb24gcmVkdWN0aW9uLCAKKipOT1QqKiBvbiB0aGUgYFVNQVBgIG9uZSAhCgpgYGB7ciBxX2NsdXN0X29uX1BDQSwgY2xhc3Muc291cmNlPSJxdWVzdGlvbiIsIGV2YWwgPSBGQUxTRX0KIyBxX2NsdXN0X29uX1BDQQoKQW55IGlkZWEgd2h5ID8KCmBgYAoKYGBge3IgYV9jbHVzdF9vbl9QQ0EsIGNsYXNzLnNvdXJjZT1jKCJmb2xkLWhpZGUiLCAiYW5zd2VyIiksIGNsYXNzLm91dHB1dD0iYW5zd2VybyJ9CiMgYV9jbHVzdF9vbl9QQ0EKCiMjIC4gVU1BUCBpcyBhIHN0cm9uZyBBUFBST1hJTUFUSU9OIG9mIHRoZSBtdWx0aWRpbWVuc2lvbmFsIHNwYWNlCiMjICAgd2hpY2ggc2luZ2xlIHB1cnBvc2UgaXMgdG8gSEVMUCB1cyB1bmRlcnN0YW5kIG1vcmUgZWFzaWx5IHRoZQojIyAgIGhpZGRlbiBzdHJ1Y3R1cmUgb2Ygb3VyIGRhdGFzZXQuCiMjCiMjIC4gVGhlIFBDQSBjb21wb25lbnQgKG9yIGFueSByZWR1Y3Rpb24gbWV0aG9kIHVzZWQgZGlyZWN0bHkgZnJvbSBvdXIKIyMgICBub3JtYWxpemVkL3NjYWxlZCBkYXRhLCBsaWtlIE1EUyBvciBJQ0EsIC4uLikgY29udGFpbnMgdGhlICJyZWFsIiAKIyMgICBpbmZvcm1hdGlvbiwganVzdCBkaXN0cmlidXRlZCBkaWZmZXJlbnRseS4KIyMKIyMgLiBJZiBvbmUgdXNlcyB0aGUgInNlY29uZCByZWR1Y2VkIHNwYWNlIiB1c2VkIGZvciB2aXN1YWxpemF0aW9uIChVTUFQLAojIyAgIHRTTkUsIERNLCAuLi4pIGFzIHRoZSBiYXNpcyBmb3IgY2x1c3RlcmluZywgb25lIG1heSBvYnRhaW4gcHJldHR5IHBsb3RzCiMjICAgd2l0aCB3ZWxsLWRlZmluZWQgY2x1c3RlcnMuIFRoZSBsYXR0ZXIgYmVpbmcgYmFzZWQgb24gd3JvbmcgZGF0YSwgdGh1cwojIyAgIGxlYWRpbmcgdG8gd3JvbmcgaW50ZXJwcmV0YXRpb24gOigKCmBgYAoKPGJyPgoKIyMgRmluZCBuZWlnaGJvcnMKCkJlZm9yZSBydW5uaW5nIHRoZSBMb3V2YWluIG1ldGhvZCwgYSBmaXJzdCBwYXNzIG1ldGhvZCBpcyB1c2VkIHRvIGdlbmVyYXRlIGEgIkstTmVhcmVzdCBOZWlnaGJvdXIiIGdyYXBoIChzZWUgbW9yZSBkZXRhaWxzIFtoZXJlXShodHRwczovL3NhdGlqYWxhYi5vcmcvc2V1cmF0L2FydGljbGVzL3BibWMza190dXRvcmlhbC5odG1sKXt0YXJnZXQ9Il9ibGFuayJ9KS4KCmBgYHtyIGZubjIwfQojIGZubjIwCgojIyBDb21wdXRlIGEgU05OIHVzaW5nIHRoZSBmaXJzdCAyMCBQQ3MKc29iaiA8LSBTZXVyYXQ6OkZpbmROZWlnaGJvcnMoCiAgb2JqZWN0ID0gc29iaiwgCiAgZGltcyA9IDE6MjAsIAogIHJlZHVjdGlvbiA9ICJwY2EiKQoKYGBgCgojIyBMb3V2YWluIGNsdXN0ZXJpbmcKCi0gICBXZSB3aWxsIHRlc3QgbXVsdGlwbGUgZGlmZmVyZW50IHJlc29sdXRpb25zCgotICAgVGhlIFNldXJhdCBmdW5jdGlvbiB0byBwZXJmb3JtIGNsdXN0ZXJpbmcgY2FuIGJlIGNhbGxlZCB3aXRoIAogICAgbXVsdGlwbGUgcmVzb2x1dGlvbnMgYXQgb25jZSAobGVzcyB0byBjb2RlLCBsdWNreSB1cyAhKS4KICAgIAo8YnI+CjxDRU5URVI+KipUZUFtV29SayBUaU1lICEqKgo8YnI+CiFbXShhMm1pYmxldWZyYWlzZTUwLmpwZykKPC9DRU5URVI+Cjxicj4KCldlIHdpbCBkaXNwYXRjaCB0aGUgZGlmZmVyZW50IGNsdXN0ZXJpbmcgcmVzb2x1dGlvbiB2YWx1ZXMgdG8gZ3JvdXBzIG9mIHRyYWluZWVzLgoKYGBge3IgY2x1c3RMMjB9CiMgY2x1c3RMMjAKCiMjIExvdXZhaW4gcmVzb2x1dGlvbnMgdG8gdGVzdApyZXNvbCA8LSBjKC4zLCAuNiwgLjksIDEuMiwgMS41LCAxLjgpCgojIyBDbHVzdGVyaW5nCnNvYmogPC0gU2V1cmF0OjpGaW5kQ2x1c3RlcnMoCiAgb2JqZWN0ID0gc29iaiwgCiAgcmVzb2x1dGlvbiA9IHJlc29sLAogIHZlcmJvc2UgPSBGQUxTRSkKCmBgYAoKPGJyPgoKKipRdWVzdGlvbioqCgpgYGB7ciBxX2NsdXN0ZGVzYywgY2xhc3Muc291cmNlPSJxdWVzdGlvbiIsIGV2YWwgPSBGQUxTRX0KIyBxX2NsdXN0ZGVzYwoKQ291bGQgeW91IHRlbGwgdXMgd2hhdCBjaGFuZ2VkIGluIG91ciBvYmplY3QgPwoKYGBgCgo8YnI+CgpgYGB7ciBhX2NsdXN0ZGVzYywgY2xhc3Muc291cmNlPWMoImZvbGQtaGlkZSIsICJhbnN3ZXIiKSwgY2xhc3Mub3V0cHV0PSJhbnN3ZXJvIiwgZXZhbCA9IEZBTFNFfQojIGFfY2x1c3RkZXNjCgojIyBQbGVhc2UsIGZvY3VzIG9uIHRoZSBbbWV0YS5kYXRhXSBzbG90ClZpZXcoc29iaikKCiMjIC4gTmV3IGVudHJpZXMgaW4gdGhlIGJhcmNvZGVzIG1ldGFkYXRhIDoKIyMgICAuIFRoZSByZXN1bHRzIG9mIG91ciBjbHVzdGVyaW5ncwojIyAgIC4gJ3NldXJhdF9jbHVzdGVycycgd2hpY2ggY29ycmVzcG9uZHMgdG8gdGhlIAojIyAgICAgbGFzdCB2ZXJzaW9uIHdlIHJhbgojIwojIyAuIFRoaXMgJ3NldXJhdF9jbHVzdGVycycgYW5ub3RhdGlvbiB3aWxsIGJlIHRoZQojIyAgIG9uZSB1c2VkIGJ5IGRlZmF1bHQgYnkgU2V1cmF0IGZvciBhbnkgZnVydGhlcgojIyAgIGFuYWx5c2lzCgpgYGAKCjxicj4KCiMjIEFzc2VzcyByZXNvbHV0aW9ucwoKIyMjIE9uIFVNQVBzCgojIyMjIENsdXN0ZXJzCgpQbG90dGluZyBVTUFQcyBoYXJib3JpbmcgdGhlIGNsdXN0ZXJpbmcgcmVzdWx0cyBmb3Igb3VyIHRlc3RlZCByZXNvbHV0aW9ucwoKYGBge3IgY2x1c3RfZGltcGxvdCwgZmlnLndpZHRoPTE4LCBmaWcuaGVpZ2h0PTl9CiMgY2x1c3RfZGltcGxvdAoKIyMgTWV0YWRhdGEgbmFtZSBvZiBjbHVzdGVyaW5nIHJlc3VsdHMgKGRlZmluZWQgYnkgZGVmYXVsdCBieSBTZXVyYXQpCnJlc29sX25hbWVzIDwtIHBhc3RlMCgiUk5BX3Nubl9yZXMuIiwgcmVzb2wpCgojIyBEaW1QbG90ClNldXJhdDo6RGltUGxvdCgKICBvYmplY3QgPSBzb2JqLCAKICByZWR1Y3Rpb24gPSAidW1hcCIsIAogIGdyb3VwLmJ5ID0gcmVzb2xfbmFtZXMsCiAgbGFiZWwgPSBUUlVFLCAKICByZXBlbCA9IFRSVUUpCgpgYGAKCjxicj4KCmBgYHtyIHFfY29tcHJlcywgY2xhc3Muc291cmNlPSJxdWVzdGlvbiIsIGV2YWwgPSBGQUxTRX0KIyBxX2NvbXByZXMKCi4gQ291bGQgeW91IGJyaWVmbHkgY29tcGFyZSB0aGUgZGlmZmVyZW50IHZlcnNpb25zID8KCi4gV291bGQgeW91IGNvbnNpZGVyIHRoYXQgc29tZSByZXN1bHRzIGFyZSB1bmRlci9vdmVyLWNsdXN0ZXJlZCA/CgouIEFyZSBhbGwgY2x1c3RlcnMgd2VsbC1kZWZpbmVkID8KCmBgYAoKPGJyPgo8YnI+CgotICAgU29tZXRoaW5nIHRoYXQgbWlnaHQgaGVscCAoYSBiaXQpIDogYHNwbGl0dGluZ2AgdGhlIGNsdXN0ZXJpbmcgcmVzdWx0cyA6CiAgICAKICAgIGBgYHtyIGNsdXN0X2RpbXBsb3Rfc3BsaXQsIGZpZy53aWR0aD0xOCwgZmlnLmhlaWdodD00fQogICAgIyBjbHVzdF9kaW1wbG90X3NwbGl0CiAgICAKICAgICMjIExvb3Bpbmcgb24gcmVzb2x1dGlvbnMKICAgIGZvciAobXlfZ3JwIGluIHJlc29sX25hbWVzKSB7CiAgICAgICMjIERpbVBsb3QKICAgICAgcCA8LSBTZXVyYXQ6OkRpbVBsb3QoCiAgICAgICAgb2JqZWN0ID0gc29iaiwgCiAgICAgICAgcmVkdWN0aW9uID0gInVtYXAiLCAKICAgICAgICBncm91cC5ieSA9IG15X2dycCwKICAgICAgICBzcGxpdC5ieSA9IG15X2dycCwKICAgICAgICBsYWJlbCA9IFRSVUUsIAogICAgICAgIHJlcGVsID0gVFJVRSkKICAgICAgcHJpbnQocCkKICAgIH0KICAgIGBgYAogICAgCjxicj4KCi0gICBTb21ldGhpbmcgb3RoZXIgOiBhc3Nlc3NpbmcgY2x1c3RlcnMgKipzdGFiaWxpdHkqKiBhY3Jvc3MgcmVzb2x1dGlvbnMsCiAgICB0aGFua3MgdG8gYGNsdXN0cmVlYCAoREVNTykgOgogICAgCiAgICBgYGB7ciBjbHVzdHJlZV9kZW1vLCBjbGFzcy5zb3VyY2UgPSAibm90cnVuIiwgY2xhc3Mub3V0cHV0ID0gIm5vdHJ1bm8iLCBmaWcud2lkdGggPSA2LCBmaWcuaGVpZ2h0ID0gOH0KICAgICMgY2x1c3RyZWVfZGVtbwogICAgCiAgICAjIyBGdWxsIGxvYWRpbmcgb2YgY2x1c3RyZWUgaXMgbmVlZGVkIGhlcmUuLi4KICAgIGxpYnJhcnkoY2x1c3RyZWUpCiAgICAKICAgICMjIFJ1bm5pbmcgY2x1c3RyZWUKICAgIGNsdXN0cmVlOjpjbHVzdHJlZSh4ID0gc29iaiwgcHJlZml4ID0gJ1JOQV9zbm5fcmVzLicpCiAgICAKICAgICMjIFVubG9hZGluZyB0aGUgY2x1c3RyZWUgcGFja2FnZQogICAgZGV0YWNoKCdwYWNrYWdlOmNsdXN0cmVlJywgdW5sb2FkID0gVFJVRSkKICAgIAogICAgYGBgCgojIyMjIENlbGwtdHlwZSBtYXJrZXJzCgpXZSBjYW4gcGxvdCB0aGUgY2VsbCBtYXJrZXJzIHdlIGFscmVhZHkga25vdwoKYGBge3IgdV9tYXJrZXJzLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9MTJ9CiMgdV9tYXJrZXJzCgojIyBNdWx0aXBsZSBGZWF0dXJlUGxvdHMgd2l0aCBtYXJrZXJzClNldXJhdDo6RmVhdHVyZVBsb3Qob2JqZWN0ID0gc29iaiwgCiAgICAgICAgICAgICAgICAgICAgZmVhdHVyZXMgPSB0ZDNhX21hcmtlcnMpICsgCiAgcGF0Y2h3b3JrOjpwbG90X2xheW91dChucm93ID0gMywgCiAgICAgICAgICAgICAgICAgICAgICAgICBuY29sID0gMykKCmBgYAoKPGJyPgoKSnVzdCBmb3IgImZ1biIgOiB3aGF0IHdvdWxkIGhhdmUgYmVlbiBzZWVuIG9uIGEgUENBID8gKERFTU8pCgpgYGB7ciB1X21hcmtlcnNfcGNhLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9MTIsIGNsYXNzLnNvdXJjZT0ibm90cnVuIiwgY2xhc3Mub3V0cHV0PSJub3RydW5vIn0KIyB1X21hcmtlcnNfcGNhCgojIyBNdWx0aXBsZSBGZWF0dXJlUGxvdHMgd2l0aCBtYXJrZXJzClNldXJhdDo6RmVhdHVyZVBsb3Qob2JqZWN0ID0gc29iaiwgCiAgICAgICAgICAgICAgICAgICAgZmVhdHVyZXMgPSB0ZDNhX21hcmtlcnMsIAogICAgICAgICAgICAgICAgICAgIHJlZHVjdGlvbiA9ICJwY2EiKSArIAogIHBhdGNod29yazo6cGxvdF9sYXlvdXQobnJvdyA9IDMsIAogICAgICAgICAgICAgICAgICAgICAgICAgbmNvbCA9IDMpCgpgYGAKCjxicj4KCiMjIyBDbHVzdGVyLXNwZWNpZmljIG1hcmtlcnMKCkEgcHJhY3RpY2FsIHdheSB0byBjaGFyYWN0ZXJpemUgb3VyIGNsdXN0ZXJpbmcgcmVzdWx0cyBpcyB0byBnZXQgYmFjayB0byBhIGxldmVsIG9mIGtub3dsZWRnZSB5b3UgYXJlIGNvbmZpZGVudCBpbiA6IG1hcmtlciBnZW5lcy4KClNldXJhdCBoYXMgYSBoYW5keSBmdW5jdGlvbiB0byA6CgotICAgSWRlbnRpZnkgZGlmZmVyZW50aWFsIGV4cHJlc3NlZCBnZW5lcyBzcGVjaWZpYyB0byBlYWNoIGFuZCBldmVyeSBwcm92aWRlZCBjYXRlZ29yeSBvZiBjZWxscyAoaGVyZSwgY2x1c3RlcmluZyByZXN1bHRzKQoKLSAgIERyYXcgYSBjbHVzdGVyaXplZCwgYW5ub3RhdGVkIGhlYXRtYXAgb2YgdGhlc2UgZ2VuZXMKCmBgYHtyIGhfZmFtLCBjbGFzcy5zb3VyY2U9Im5vdHJ1biIsIGNsYXNzLm91dHB1dD0ibm90cnVubyIsIGV2YWwgPSBGQUxTRX0KIyBoX2ZhbQoKP1NldXJhdDo6RmluZEFsbE1hcmtlcnMKCmBgYAoKYGBge3IgZGhtX3ByZXAsIGZpZy53aWR0aD0yNCwgZmlnLmhlaWdodD04fQojIGRobV9wcmVwCgojIyBMb29waW5nIG9uIGNsdXN0ZXJpbmcgcmVzdWx0cwpmYW1fYWxsIDwtIGxhcHBseShyZXNvbF9uYW1lcywgZnVuY3Rpb24ocikgewogIAogICMjIEZpbmQgbWFya2VycyBmb3IgYWxsIGNsdXN0ZXJzCiAgIyMgQSBzZWVkIGlzIG5lZWRlZCBoZXJlICEKICBTZXVyYXQ6OklkZW50cyhvYmplY3QgPSBzb2JqKSA8LSBzb2JqW1tyXV1bWzFdXQogIGZhbSA8LSBTZXVyYXQ6OkZpbmRBbGxNYXJrZXJzKAogICAgb2JqZWN0ID0gc29iaiwgCiAgICBsb2dmYy50aHJlc2hvbGQgPSAuNSwgCiAgICBvbmx5LnBvcyA9IFRSVUUsIAogICAgbWluLnBjdCA9IC41LCAKICAgIHZlcmJvc2UgPSBGQUxTRSwKICAgIHJhbmRvbS5zZWVkID0gbXlfc2VlZCkKICAKICAjIyBHZXQgdG9wIGdlbmUgcGVyIGNsdXN0ZXIKICBmYW10b3AgPC0gZmFtJGdlbmVbIWR1cGxpY2F0ZWQoZmFtJGNsdXN0ZXIpXQogIHZsbl9wbCA8LSBTZXVyYXQ6OlZsblBsb3Qob2JqZWN0ID0gc29iaiwgZmVhdHVyZXMgPSBmYW10b3ApCiAgcHJpbnQodmxuX3BsKQogIAogICMjIFNlbGVjdCB0b3AxMCBnZW5lcyB3aGVuIGF2YWlsYWJsZQogIGZhbV9yZHggPC0gZHBseXI6Omdyb3VwX2J5KC5kYXRhID0gZmFtLCBjbHVzdGVyKQogIGZhbV9yZHggPC0gZHBseXI6OmZpbHRlciguZGF0YSA9IGZhbV9yZHgsIGF2Z19sb2cyRkMgPiAxKQogIGZhbV9yZHggPC0gZHBseXI6OnNsaWNlX2hlYWQoLmRhdGEgPSBmYW1fcmR4LCBuID0gMTApCiAgZGggPC0gU2V1cmF0OjpEb0hlYXRtYXAoCiAgICBvYmplY3QgPSBzb2JqLCBmZWF0dXJlcyA9IGZhbV9yZHgkZ2VuZSwgc2l6ZSA9IDMsCiAgICBjb21iaW5lID0gVFJVRSkgKyBnZ3Bsb3QyOjpnZ3RpdGxlKGxhYmVsID0gcikKICByZXR1cm4oZGgpCn0pCgoKYGBgCgpgYGB7ciBkaG1fcGxvdCwgZmlnLndpZHRoPTI0LCBmaWcuaGVpZ2h0PTEyfQojIGRobV9wbG90CgojIyBQbG90IGFsbCBoZWF0bWFwcyBhdCBvbmNlCnBhdGNod29yazo6d3JhcF9wbG90cyhmYW1fYWxsKSArIHBhdGNod29yazo6cGxvdF9sYXlvdXQobnJvdyA9IDIpCgpgYGAKCjxicj4KCioqUXVlc3Rpb25zKiogOiBDb21wYXJpbmcgdGhlIGhlYXRtYXBzIDoKCmBgYHtyIHFfaG0xLCBjbGFzcy5zb3VyY2U9InF1ZXN0aW9uIiwgZXZhbCA9IEZBTFNFfQojIHFfaG0xCgpXaGljaCByZXNvbHV0aW9uIHdvdWxkIHlvdSBjaG9vc2UsIGFuZCB3aHkgPwoKYGBgCgo8YnI+CgpgYGB7ciBxX2htMiwgY2xhc3Muc291cmNlPSJxdWVzdGlvbiIsIGV2YWwgPSBGQUxTRX0KIyBxX2htMgoKSXMgdGhlcmUgYSBzaW5nbGUgb25lIGFuZCBvbmx5IGFuc3dlciB0byB0aGUgZm9ybWVyIHF1ZXN0aW9uID8KCmBgYAoKPGJyPgoKIyMjIENsdXN0ZXJzIGNvbnRpbmdlbmNpZXMgYW5kIHByb3BvcnRpb25zCgpPbmUgY2FuIG9ic2VydmUgaG93IG1hbnkgY2VsbHMgYXJlIGluIGVhY2ggY2x1c3RlciwgYW5kIHdoYXQgcHJvcG9ydGlvbiBvZiBhbGwgY2VsbHMgdGhlc2UgcmVwcmVzZW50IChERU1PKQoKYGBge3IgY2x1c3RfcG9wLCBjbGFzcy5zb3VyY2U9Im5vdHJ1biIsIGNsYXNzLm91dHB1dD0ibm90cnVubyJ9CiMgY2x1c3Rwb3AKCiMjIExvb3Bpbmcgb24gcmVzb2x1dGlvbnMKZm9yICh4IGluIHJlc29sX25hbWVzKSB7CiAgIyMgQ29udGluZ2VuY2llcwogIHByaW50KHRhYmxlKHNvYmpbW3hdXSkpCiAgIyMgUHJvcG9ydGlvbnMKICBwcmludChmb3JtYXQodGFibGUoc29ialtbeF1dKSAvIG5jb2woc29iaiksIGRpZ2l0cyA9IDIpKQogIGNhdCgnXG5cbicpCn0KCmBgYAoKPGJyPgoKCiMjIFNlbGVjdGlvbgoKRm9yIHRoZSBkb3duc3RyZWFtIGFuYWx5c2VzLCB3ZSB3aWxsIHVzZSB0aGUgcmVzb2x1dGlvbiAqKmAwLjhgKiouCgpXZSB3aWxsIGZpeCB0aGUgY2x1c3RlcmluZyByZXN1bHRzIGZvciB0aGlzIHJlc29sdXRpb24gYXMgdGhlIGRlZmF1bHQgb25lClNldXJhdCB3aWxsIHVzZSBmb3IgYW55IGZ1cnRoZXIgYW5hbHlzZXMgLyBwbG90cywgdXNpbmcgdGhlIGBTZXVyYXQ6OklkZW50cygpYApmdW5jdGlvbi4KCmBgYHtyIHNlbF9yZXMsIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTZ9CiMgc2VsX3JlcwoKIyMgRml4aW5nIGxfcmVzCmxfcmVzIDwtIC44CgojIyBQZXJmb3JtaW5nIExvdXZhaW4gY2x1c3RlcmluZyBhdCB0aGUgc2VsZWN0ZWQgcmVzb2x1dGlvbgpzb2JqIDwtIFNldXJhdDo6RmluZENsdXN0ZXJzKAogIG9iamVjdCA9IHNvYmosIAogIHJlc29sdXRpb24gPSBsX3JlcywKICB2ZXJib3NlID0gRkFMU0UpCgojIyBDaGVjayBvbiBkZWZhdWx0ICJJZGVudHMiCmlkZW50aWNhbCgKICB4ID0gU2V1cmF0T2JqZWN0OjpGZXRjaERhdGEoCiAgICBvYmplY3QgPSBzb2JqLCAKICAgIHZhcnMgPSBwYXN0ZTAoIlJOQV9zbm5fcmVzLiIsIGxfcmVzKSlbWzFdXSwKICB5ID0gdW5uYW1lKFNldXJhdDo6SWRlbnRzKG9iamVjdCA9IHNvYmopKQopCgojIyBEaW1QbG90IHdpdGhvdXQgc3BlY2lmeWluZyB0aGUgcmVzb2x1dGlvbgpTZXVyYXQ6OkRpbVBsb3QoCiAgb2JqZWN0ID0gc29iaiwgCiAgcmVkdWN0aW9uID0gInVtYXAiLCAKICBsYWJlbCA9IFRSVUUsIAogIHJlcGVsID0gVFJVRSkKCmBgYAoKPGJyPgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCjxicj4KCiMgU2F2ZSB0aGUgU2V1cmF0IG9iamVjdAoKV2Ugd2lsbCBzYXZlIG91ciBTZXVyYXQgb2JqZWN0IHRoYXQgbm93IGNvbnRhaW5zIG91ciBjbHVzdGVyaW5nIHJlc3VsdHMgIDoKCmBgYHtyIHNhdmVyZHMyLCBmb2xkLm91dHB1dCA9IEZBTFNFfQojIHNhdmVyZHMyCgojIyBTYXZlIG91ciBTZXVyYXQgb2JqZWN0IChyaWNoIG5hbWluZykKb3V0X25hbWUgPC0gcGFzdGUwKAogICAgICAgICAgb3V0cHV0X2RpciwgIi8iLCBwYXN0ZSgKICAgICAgICAgICAgYygiMTAiLCBTZXVyYXQ6OlByb2plY3Qoc29iaiksICJTNSIsIAogICAgICAgICAgICAgIHBhc3RlMCgKICAgICAgICAgICAgICAgICJDbHVzdGVyZWQuIiwKICAgICAgICAgICAgICAgIGxfcmVzKSwgCiAgICAgICAgICAgICAgcGFzdGUoCiAgICAgICAgICAgICAgICBkaW0oc29iaiksIAogICAgICAgICAgICAgICAgY29sbGFwc2UgPSAnLicKICAgICAgICAgICAgICApCiAgICAgICAgICAgICksIGNvbGxhcHNlID0gIl8iKSwKICAgICAgICAgICAgIi5SRFMiKQoKIyMgQ2hlY2sKcHJpbnQob3V0X25hbWUpCgojIyBXcml0ZSBvbiBkaXNrCnNhdmVSRFMob2JqZWN0ID0gc29iaiwgCiAgICAgICAgZmlsZSA9IG91dF9uYW1lKQpgYGAKCjxicj4KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgo8YnI+PGJyPjxicj4KCiMgUnNlc3Npb24KCmBgYHtyIHJzZXNzaW9uLCBjbGFzcy5zb3VyY2U9Im5vdHJ1biIsIGNsYXNzLm91dHB1dD0ibm90cnVubyJ9CiMgcnNlc3Npb24KCnV0aWxzOjpzZXNzaW9uSW5mbygpCgpgYGAK