J. Taroni 2018
In this notebook, we’ll be evaluating reconstruction of E-MTAB-2452
sorted peripheral blood cells (CD4+ T cells, CD14+ monocytes, CD16+ neutrophils) profiled on microarray from several autoimmune diseases. as compared to isolated immune cell data included in the recount2. SRP045500
is a comparison of 6 cell subsets in “immune-associated diseases.”
Functions and directory set up
`%>%` <- dplyr::`%>%`
Warning messages:
1: In scan(file = file, what = what, sep = sep, quote = quote, dec = dec, :
EOF within quoted string
2: In scan(file = file, what = what, sep = sep, quote = quote, dec = dec, :
EOF within quoted string
3: In scan(file = file, what = what, sep = sep, quote = quote, dec = dec, :
EOF within quoted string
4: In scan(file = file, what = what, sep = sep, quote = quote, dec = dec, :
EOF within quoted string
5: In scan(file = file, what = what, sep = sep, quote = quote, dec = dec, :
EOF within quoted string
6: In scan(file = file, what = what, sep = sep, quote = quote, dec = dec, :
EOF within quoted string
7: In scan(file = file, what = what, sep = sep, quote = quote, dec = dec, :
EOF within quoted string
# custom functions
source(file.path("util", "plier_util.R"))
# Noticed I needed this functionality repeatedly
ReconstructionEvalWrapper <- function(z.mat, b.mat, input.exprs,
dataset.name, lv.type = "All") {
# This function is a wrapper for reconstructing gene expression data given
# gene loadings (Z) and LVs (B) from a PLIER model. It also takes the
# input expression data so evaluations (Spearman correlation & MASE) can be
# performed. Finally, it returns a tidy data.frame of these measures.
# See also: the compare reconstructed to input section of util/plier_util.R
#
# Args:
# z.mat: a matrix of gene loadings from PLIER
# b.mat: a matrix of latent variables from PLIER
# input.exprs: gene expression matrix before reconstruction (input into
# PLIER), used as the `true.mat` argument for the reconstruction
# evaluations (should be row-normalized, can use
# GetOrderedRowNorm to obtain this matrix)
# dataset.name: a string that indicates the dataset being reconstructed,
# this will end up being the value in the `Dataset` column of
# data.frame returned by this function
# lv.type: a string that indicates what LVs were use for reconstruction,
# this will end up being the value in the
# `LVs used in reconstruction` column of the data.frame returned
#
# Returns:
# recon.eval.df: A data.frame with the following columns - Sample, MASE,
# Spearman correlation, LVs used in reconstruction, Dataset
#
## Reconstruction
recon.exprs <- GetReconstructedExprs(z.matrix = z.mat,
b.matrix = b.mat)
## Evaluations
recon.error <- GetReconstructionMASE(true.mat = input.exprs,
recon.mat = recon.exprs)
recon.cor <- GetReconstructionCorrelation(true.mat = input.exprs,
recon.mat = recon.exprs)
## get data.frame
num.samples <- ncol(input.exprs)
recon.eval.df <- as.data.frame(cbind(colnames(input.exprs),
recon.error, recon.cor,
rep(lv.type, num.samples),
rep(dataset.name, num.samples)))
colnames(recon.eval.df) <- c("Sample", "MASE", "Spearman correlation",
"LVs used in reconstruction", "Dataset")
recon.eval.df <- recon.eval.df %>%
dplyr::mutate(MASE = as.numeric(as.character(MASE)),
`Spearman correlation` =
as.numeric(as.character(`Spearman correlation`)))
return(recon.eval.df)
}
# plot and result directory setup for this notebook
plot.dir <- file.path("plots", "04")
dir.create(plot.dir, recursive = TRUE, showWarnings = FALSE)
results.dir <- file.path("results", "04")
dir.create(results.dir, recursive = TRUE, showWarnings = FALSE)
Load data
E-MTAB-2452
Expression data
gs.file <- file.path("data", "expression_data",
"E-MTAB-2452_hugene11st_SCANfast_with_GeneSymbol.pcl")
exprs.df <-readr::read_tsv(gs.file)
Parsed with column specification:
cols(
.default = col_double(),
EntrezID = col_integer(),
GeneSymbol = col_character()
)
See spec(...) for full column specifications.
# drop gene identifiers
exprs.mat <- as.matrix(exprs.df[, 3:ncol(exprs.df)])
rownames(exprs.mat) <- exprs.df$GeneSymbol
rm(exprs.df)
B matrix
From recount2 model
iso.b.file <- file.path("results", "03",
"E-MTAB-2452_B_matrix_recount2_model.txt")
iso.b.matrix <- read.delim(iso.b.file)
recount2
PLIER model
plier.results <- readRDS(file.path("data", "recount2_PLIER_data",
"recount_PLIER_model.RDS"))
Evaluation of reconstruction
Spearman correlation, MASE
eval.file <- file.path("results", "02",
"recount2_recount2_model_recon_eval_df.tsv")
recount.eval.df <- readr::read_tsv(eval.file)
Parsed with column specification:
cols(
Sample = col_character(),
MASE = col_double(),
`Spearman correlation` = col_double(),
`LVs used in reconstruction` = col_character()
)
recount.eval.df$Dataset <- rep("recount2", nrow(recount.eval.df))
Identify the samples that belong to SRP045500
, we’ll duplicate these values in recount.eval.df
so we can plot them separately.
srp.df <- recount.eval.df %>%
dplyr::filter(grepl("SRP045500", recount.eval.df$Sample)) %>%
dplyr::mutate(Dataset = "SRP045500")
recount.eval.df <- dplyr::bind_rows(recount.eval.df, srp.df)
Reconstruction of E-MTAB-2452 with all LVs
# recount2 model Z matrix
z.matrix <- plier.results$Z
Reconstruction and evaluation
We need to obtain a (row-normalized) gene expression matrix as the “true” expression matrix. We use this for evaluation with Spearman and MASE as in previous notebooks. We’ll use this for the other evaluations (e.g., pathway-associated LVs only) as well.
iso.ord.rownorm <- GetOrderedRowNorm(exprs.mat = exprs.mat,
plier.model = plier.results) #recount2
iso.recon.all.df <- ReconstructionEvalWrapper(z.mat = as.matrix(z.matrix),
b.mat = as.matrix(iso.b.matrix),
input.exprs = iso.ord.rownorm,
lv.type = paste("All, n =",
ncol(z.matrix)),
dataset.name = "E-MTAB-2452")
head(iso.recon.all.df)
Plotting
Add the isolated cell type reconstruction evaluation measures to the recount data.frame
eval.df <- dplyr::bind_rows(recount.eval.df, iso.recon.all.df)
binding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vector
rm(iso.recon.all.df)
Correlation
dplyr::filter(eval.df, `LVs used in reconstruction` ==
paste("All, n =", ncol(z.matrix))) %>%
ggplot2::ggplot(ggplot2::aes(x = `Spearman correlation`,
group = Dataset, fill = Dataset)) +
ggplot2::geom_density(alpha = 0.3) +
ggplot2::theme_bw() +
ggplot2::scale_fill_manual(values = c("white", "gray50", "black")) +
ggplot2::ggtitle(paste("All, n =", ncol(z.matrix)))

MASE
dplyr::filter(eval.df, `LVs used in reconstruction` ==
paste("All, n =", ncol(z.matrix))) %>%
ggplot2::ggplot(ggplot2::aes(x = MASE,
group = Dataset, fill = Dataset)) +
ggplot2::geom_density(alpha = 0.3) +
ggplot2::theme_bw() +
ggplot2::scale_fill_manual(values = c("white", "gray50", "black")) +
ggplot2::ggtitle(paste("All, n =", ncol(z.matrix)))

Summary
The isolated immune cell samples (SRP045500
) are among some of the best reconstructed (high correlation, low error) in the recount2 compendium. E-MTAB-2452
, the microarray dataset, is reconstructed relatively poorly. This is unsurprising, as it was not included in training and is from a different technology. We’ll look next at reconstruction using subsets of the recount2 PLIER model LVs. Specifically, we hypothesize that reconstruction using only pathway-associated LVs (e.g., those LVs rooted in biological signal) may make the measures of reconstruction performance – correlation and error – more similar between the two isolated immune cell datasets. If this is the case, it supports the idea that the PLIER-learned LVs that are not associated with pathways are, at least in part, capturing variation that can be attributed to technical factors. We would expect that the technical signals in recount2 would be distinct (e.g., an LV associated with library prep strategy) from what’s in E-MTAB-2452
and, therefore, that including this information may negatively impact reconstruction of this dataset.
Reconstruction of E-MTAB-2452 with pathway-associated LVs only
# Get the significant LVs (from recount2 model) using 0.05 as the FDR cutoff
plier.summary <- plier.results$summary
sig.summary <- plier.summary %>%
dplyr::filter(FDR < 0.05)
sig.lvs <- unique(sig.summary$`LV index`)
# drop columns (LVs) from Z that are not significantly associated with prior
# info
sig.z.mat <- z.matrix[, as.integer(sig.lvs)]
# drop rows (LVs) from E-MTAB-2452 B that are not significantly associated with
# prior info
iso.sig.b.mat <- as.matrix(iso.b.matrix[as.integer(sig.lvs), ])
Reconstruction and evaluation
path.lv.type <- paste("Pathway-associated, n =", ncol(sig.z.mat))
iso.recon.sig.df <- ReconstructionEvalWrapper(z.mat = as.matrix(sig.z.mat),
b.mat = iso.sig.b.mat,
input.exprs = iso.ord.rownorm,
dataset.name = "E-MTAB-2452",
lv.type = path.lv.type)
head(iso.recon.sig.df)
Add to rest of evaluations
eval.df <- dplyr::bind_rows(eval.df, iso.recon.sig.df)
binding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vector
rm(iso.recon.sig.df)
Reconstruction with LVs that are not associated with pathways
Here, we’ll have to reconstruct the recount2 data with the LVs that are not significantly associated with pathways, as we did not perform this analysis in 02-recount2_PLIER_exploration
.
# some LVs don't even make it to the summary data.frame from the PLIER model
# the LVs we want here are *all* the LVs that do not have at least one pathway
# significantly associated with them
all(1:nrow(plier.results$B) %in% unique(plier.summary$`LV index`))
[1] FALSE
# non-significant LVs, then, are all LVs that are not in the list of
# pathway-associated LVs
non.lvs <- setdiff(1:nrow(plier.results$B), as.integer(sig.lvs))
# z matrix for just these LVs
non.z.mat <- z.matrix[, as.integer(non.lvs)]
recount2
# get B matrix
non.b.mat <- as.matrix(plier.results$B[as.integer(non.lvs), ])
# reconstruction & evaluation
non.lv.type <- paste("LVs not associated with pathway, n =", length(non.lvs))
recount.recon.non.df <-
ReconstructionEvalWrapper(z.mat = as.matrix(non.z.mat),
b.mat = non.b.mat,
input.exprs = recount.input.exprs,
lv.type = non.lv.type,
dataset.name = "recount2")
head(recount.recon.non.df)
SRP045500
srp.non.df <- recount.recon.non.df %>%
dplyr::filter(grepl("SRP045500",
recount.recon.non.df$Sample)) %>%
dplyr::mutate(Dataset = "SRP045500")
recount.recon.non.df <- dplyr::bind_rows(recount.recon.non.df, srp.non.df)
binding factor and character vector, coercing into character vectorbinding character and factor vector, coercing into character vector
eval.df <- dplyr::bind_rows(eval.df, recount.recon.non.df)
binding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vector
rm(recount.recon.non.df, srp.non.df)
E-MTAB-2452
# get b matrix
iso.non.b.mat <- as.matrix(iso.b.matrix[as.integer(non.lvs), ])
# reconstruction & evaluation
iso.recon.non.df <-
ReconstructionEvalWrapper(z.mat = as.matrix(non.z.mat),
b.mat = iso.non.b.mat,
input.exprs = iso.ord.rownorm,
lv.type = non.lv.type,
dataset.name = "E-MTAB-2452")
head(iso.recon.non.df)
eval.df <- dplyr::bind_rows(eval.df, iso.recon.non.df)
binding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vector
rm(iso.recon.non.df)
Plotting
Plot all three conditions on the same plot for easy comparison
Density
Correlation
ggplot2::ggplot(eval.df,
ggplot2::aes(x = `Spearman correlation`, group = Dataset, fill = Dataset)) +
ggplot2::facet_wrap(~ `LVs used in reconstruction`, ncol = 1) +
ggplot2::geom_density(alpha = 0.3) +
ggplot2::theme_bw() +
ggplot2::scale_fill_manual(values = c("white", "gray50", "black"))

plot.file <- file.path(plot.dir,
"recount2_model_isolated_cell_recon_correlation.pdf")
ggplot2::ggsave(filename = plot.file, plot = ggplot2::last_plot(),
height = 11, width = 8.5)
Error
ggplot2::ggplot(eval.df,
ggplot2::aes(x = MASE, group = Dataset, fill = Dataset)) +
ggplot2::facet_wrap(~ `LVs used in reconstruction`, ncol = 1) +
ggplot2::geom_density(alpha = 0.3) +
ggplot2::theme_bw() +
ggplot2::scale_fill_manual(values = c("white", "gray50", "black"))

plot.file <- file.path(plot.dir,
"recount2_model_isolated_cell_recon_error.pdf")
ggplot2::ggsave(filename = plot.file, plot = ggplot2::last_plot(),
height = 11, width = 8.5)
E-MTAB-2452 Boxplots
array.eval.df <- dplyr::filter(eval.df, Dataset == "E-MTAB-2452")
Correlation
ggplot2::ggplot(array.eval.df,
ggplot2::aes(x = `LVs used in reconstruction`,
y = `Spearman correlation`)) +
ggplot2::geom_boxplot() +
ggplot2::theme_bw() +
ggplot2::scale_x_discrete(labels = c("All", "Not pathway-associated",
"Pathway-associated")) +
ggplot2::ggtitle("E-MTAB-2452 reconstruction with recount2 model")

plot.file <- file.path(plot.dir,
"E-MTAB-2452_reconstruction_corr_recount2_model.pdf")
ggplot2::ggsave(plot.file, plot = ggplot2::last_plot())
Saving 7 x 7 in image
pairwise.t.test(x = array.eval.df$`Spearman correlation`,
g = array.eval.df$`LVs used in reconstruction`,
p.adjust.method = "bonferroni")
Pairwise comparisons using t tests with pooled SD
data: array.eval.df$`Spearman correlation` and array.eval.df$`LVs used in reconstruction`
All, n = 987
LVs not associated with pathway, n = 788 <2e-16
Pathway-associated, n = 199 <2e-16
LVs not associated with pathway, n = 788
LVs not associated with pathway, n = 788 -
Pathway-associated, n = 199 <2e-16
P value adjustment method: bonferroni
Error
ggplot2::ggplot(array.eval.df,
ggplot2::aes(x = `LVs used in reconstruction`, y = MASE)) +
ggplot2::geom_boxplot() +
ggplot2::theme_bw() +
ggplot2::scale_x_discrete(labels = c("All", "Not pathway-associated",
"Pathway-associated")) +
ggplot2::ggtitle("E-MTAB-2452 reconstruction with recount2 model")

plot.file <- file.path(plot.dir,
"E-MTAB-2452_reconstruction_error_recount2_model.pdf")
ggplot2::ggsave(plot.file, plot = ggplot2::last_plot())
Saving 7 x 7 in image
pairwise.t.test(x = array.eval.df$MASE,
g = array.eval.df$`LVs used in reconstruction`,
p.adjust.method = "bonferroni")
Pairwise comparisons using t tests with pooled SD
data: array.eval.df$MASE and array.eval.df$`LVs used in reconstruction`
All, n = 987
LVs not associated with pathway, n = 788 <2e-16
Pathway-associated, n = 199 <2e-16
LVs not associated with pathway, n = 788
LVs not associated with pathway, n = 788 -
Pathway-associated, n = 199 <2e-16
P value adjustment method: bonferroni
Summary
Reconstruction of a test dataset, E-MTAB-2452
, using only PLIER latent variables that significantly associated with a pathway or gene set is better in terms of both MASE and Spearman correlation than when using only latent variables that are not associated with any prior information. This is despite the fact that there are more non-significant LVs (n = 788
vs. n = 199
). However, neither of these subsets of LVs outperforms using all LVs and the performance gap between reconstructing E-MTAB-2452
and SRP045500
(the sorted immune cell dataset included in recount2) is not considerably reduced when using only pathway-associated LVs, which refutes our initial hypothesis. It’s also interesting that, when reconstructing using only non-significant LVs, the pre- and post-reconstruction correlation values are much more similar between E-MTAB-2452
and SRP045500
. This suggests that the pathway-associated LVs are important for accurate reconstruction of the SRP045500
dataset.
It’s worth noting that these sorted immune cell datasets are from patients with different diagnoses and contain different cell type populations (in addition to different technologies) that may be more or less well-represented in the recount2 compendium. We expect that these differences contribute to the results. Future analyses may focus on a microarray dataset more comparable to SRP045500
in these ways.
LS0tCnRpdGxlOiAiSXNvbGF0ZWQgaW1tdW5lIGNlbGwgcmVjb25zdHJ1Y3Rpb24iCm91dHB1dDogICAKICBodG1sX25vdGVib29rOiAKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCi0tLQoqKkouIFRhcm9uaSAyMDE4KioKCkluIHRoaXMgbm90ZWJvb2ssIHdlJ2xsIGJlIGV2YWx1YXRpbmcgcmVjb25zdHJ1Y3Rpb24gb2YgW2BFLU1UQUItMjQ1MmBdKGh0dHBzOi8vd3d3LmViaS5hYy51ay9hcnJheWV4cHJlc3MvZXhwZXJpbWVudHMvRS1NVEFCLTI0NTIvKSAKc29ydGVkIHBlcmlwaGVyYWwgYmxvb2QgY2VsbHMgKENENCsgVCBjZWxscywgQ0QxNCsgbW9ub2N5dGVzLCBDRDE2KyBuZXV0cm9waGlscykKcHJvZmlsZWQgb24gbWljcm9hcnJheSBmcm9tIHNldmVyYWwgYXV0b2ltbXVuZSBkaXNlYXNlcy4KYXMgY29tcGFyZWQgdG8gaXNvbGF0ZWQgaW1tdW5lIGNlbGwgZGF0YSBpbmNsdWRlZCBpbiB0aGUgcmVjb3VudDIuCltgU1JQMDQ1NTAwYF0oaHR0cHM6Ly90cmFjZS5uY2JpLm5sbS5uaWguZ292L1RyYWNlcy9zcmEvP3N0dWR5PVNSUDA0NTUwMCkgaXMgYSAKY29tcGFyaXNvbiBvZiA2IGNlbGwgc3Vic2V0cyBpbiAiaW1tdW5lLWFzc29jaWF0ZWQgZGlzZWFzZXMuIgoKIyMgRnVuY3Rpb25zIGFuZCBkaXJlY3Rvcnkgc2V0IHVwCgpgYGB7cn0KYCU+JWAgPC0gZHBseXI6OmAlPiVgCiMgY3VzdG9tIGZ1bmN0aW9ucwpzb3VyY2UoZmlsZS5wYXRoKCJ1dGlsIiwgInBsaWVyX3V0aWwuUiIpKQpgYGAKYGBge3J9CiMgTm90aWNlZCBJIG5lZWRlZCB0aGlzIGZ1bmN0aW9uYWxpdHkgcmVwZWF0ZWRseQpSZWNvbnN0cnVjdGlvbkV2YWxXcmFwcGVyIDwtIGZ1bmN0aW9uKHoubWF0LCBiLm1hdCwgaW5wdXQuZXhwcnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YXNldC5uYW1lLCBsdi50eXBlID0gIkFsbCIpIHsKICAjIFRoaXMgZnVuY3Rpb24gaXMgYSB3cmFwcGVyIGZvciByZWNvbnN0cnVjdGluZyBnZW5lIGV4cHJlc3Npb24gZGF0YSBnaXZlbgogICMgZ2VuZSBsb2FkaW5ncyAoWikgYW5kIExWcyAoQikgZnJvbSBhIFBMSUVSIG1vZGVsLiBJdCBhbHNvIHRha2VzIHRoZQogICMgaW5wdXQgZXhwcmVzc2lvbiBkYXRhIHNvIGV2YWx1YXRpb25zIChTcGVhcm1hbiBjb3JyZWxhdGlvbiAmIE1BU0UpIGNhbiBiZQogICMgcGVyZm9ybWVkLiBGaW5hbGx5LCBpdCByZXR1cm5zIGEgdGlkeSBkYXRhLmZyYW1lIG9mIHRoZXNlIG1lYXN1cmVzLgogICMgU2VlIGFsc286IHRoZSBjb21wYXJlIHJlY29uc3RydWN0ZWQgdG8gaW5wdXQgc2VjdGlvbiBvZiB1dGlsL3BsaWVyX3V0aWwuUgogICMKICAjIEFyZ3M6IAogICMgICB6Lm1hdDogYSBtYXRyaXggb2YgZ2VuZSBsb2FkaW5ncyBmcm9tIFBMSUVSCiAgIyAgIGIubWF0OiBhIG1hdHJpeCBvZiBsYXRlbnQgdmFyaWFibGVzIGZyb20gUExJRVIKICAjICAgaW5wdXQuZXhwcnM6IGdlbmUgZXhwcmVzc2lvbiBtYXRyaXggYmVmb3JlIHJlY29uc3RydWN0aW9uIChpbnB1dCBpbnRvIAogICMgICAgICAgICAgICAgICBQTElFUiksIHVzZWQgYXMgdGhlIGB0cnVlLm1hdGAgYXJndW1lbnQgZm9yIHRoZSByZWNvbnN0cnVjdGlvbgogICMgICAgICAgICAgICAgICBldmFsdWF0aW9ucyAoc2hvdWxkIGJlIHJvdy1ub3JtYWxpemVkLCBjYW4gdXNlCiAgIyAgICAgICAgICAgICAgIEdldE9yZGVyZWRSb3dOb3JtIHRvIG9idGFpbiB0aGlzIG1hdHJpeCkKICAjICAgZGF0YXNldC5uYW1lOiBhIHN0cmluZyB0aGF0IGluZGljYXRlcyB0aGUgZGF0YXNldCBiZWluZyByZWNvbnN0cnVjdGVkLAogICMgICAgICAgICAgICAgICAgIHRoaXMgd2lsbCBlbmQgdXAgYmVpbmcgdGhlIHZhbHVlIGluIHRoZSBgRGF0YXNldGAgY29sdW1uIG9mCiAgIyAgICAgICAgICAgICAgICAgZGF0YS5mcmFtZSByZXR1cm5lZCBieSB0aGlzIGZ1bmN0aW9uCiAgIyAgIGx2LnR5cGU6IGEgc3RyaW5nIHRoYXQgaW5kaWNhdGVzIHdoYXQgTFZzIHdlcmUgdXNlIGZvciByZWNvbnN0cnVjdGlvbiwKICAjICAgICAgICAgICAgdGhpcyB3aWxsIGVuZCB1cCBiZWluZyB0aGUgdmFsdWUgaW4gdGhlIAogICMgICAgICAgICAgICBgTFZzIHVzZWQgaW4gcmVjb25zdHJ1Y3Rpb25gIGNvbHVtbiBvZiB0aGUgZGF0YS5mcmFtZSByZXR1cm5lZAogICMKICAjIFJldHVybnM6CiAgIyAgIHJlY29uLmV2YWwuZGY6IEEgZGF0YS5mcmFtZSB3aXRoIHRoZSBmb2xsb3dpbmcgY29sdW1ucyAtIFNhbXBsZSwgTUFTRSwgCiAgIyAgICAgICAgICAgICAgICAgIFNwZWFybWFuIGNvcnJlbGF0aW9uLCBMVnMgdXNlZCBpbiByZWNvbnN0cnVjdGlvbiwgRGF0YXNldAogICMKICAKICAjIyBSZWNvbnN0cnVjdGlvbgogIHJlY29uLmV4cHJzIDwtIEdldFJlY29uc3RydWN0ZWRFeHBycyh6Lm1hdHJpeCA9IHoubWF0LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYi5tYXRyaXggPSBiLm1hdCkKICAKICAjIyBFdmFsdWF0aW9ucwogIHJlY29uLmVycm9yIDwtIEdldFJlY29uc3RydWN0aW9uTUFTRSh0cnVlLm1hdCA9IGlucHV0LmV4cHJzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWNvbi5tYXQgPSByZWNvbi5leHBycykKICByZWNvbi5jb3IgPC0gR2V0UmVjb25zdHJ1Y3Rpb25Db3JyZWxhdGlvbih0cnVlLm1hdCA9IGlucHV0LmV4cHJzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlY29uLm1hdCA9IHJlY29uLmV4cHJzKQogIAogICMjIGdldCBkYXRhLmZyYW1lCiAgbnVtLnNhbXBsZXMgPC0gbmNvbChpbnB1dC5leHBycykKICByZWNvbi5ldmFsLmRmIDwtIGFzLmRhdGEuZnJhbWUoY2JpbmQoY29sbmFtZXMoaW5wdXQuZXhwcnMpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVjb24uZXJyb3IsIHJlY29uLmNvciwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcChsdi50eXBlLCBudW0uc2FtcGxlcyksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXAoZGF0YXNldC5uYW1lLCBudW0uc2FtcGxlcykpKQogIGNvbG5hbWVzKHJlY29uLmV2YWwuZGYpIDwtIGMoIlNhbXBsZSIsICJNQVNFIiwgIlNwZWFybWFuIGNvcnJlbGF0aW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJMVnMgdXNlZCBpbiByZWNvbnN0cnVjdGlvbiIsICJEYXRhc2V0IikKICByZWNvbi5ldmFsLmRmIDwtIHJlY29uLmV2YWwuZGYgJT4lCiAgICAgICAgICAgICAgICAgICAgZHBseXI6Om11dGF0ZShNQVNFID0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoTUFTRSkpLAogICAgICAgICAgICAgICAgICAgIGBTcGVhcm1hbiBjb3JyZWxhdGlvbmAgPSAKICAgICAgICAgICAgICAgICAgICAgIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGBTcGVhcm1hbiBjb3JyZWxhdGlvbmApKSkKICAKICByZXR1cm4ocmVjb24uZXZhbC5kZikKfQpgYGAKCmBgYHtyfQojIHBsb3QgYW5kIHJlc3VsdCBkaXJlY3Rvcnkgc2V0dXAgZm9yIHRoaXMgbm90ZWJvb2sKcGxvdC5kaXIgPC0gZmlsZS5wYXRoKCJwbG90cyIsICIwNCIpCmRpci5jcmVhdGUocGxvdC5kaXIsIHJlY3Vyc2l2ZSA9IFRSVUUsIHNob3dXYXJuaW5ncyA9IEZBTFNFKQpyZXN1bHRzLmRpciA8LSBmaWxlLnBhdGgoInJlc3VsdHMiLCAiMDQiKQpkaXIuY3JlYXRlKHJlc3VsdHMuZGlyLCByZWN1cnNpdmUgPSBUUlVFLCBzaG93V2FybmluZ3MgPSBGQUxTRSkKYGBgCgojIyBMb2FkIGRhdGEKCiMjIyBFLU1UQUItMjQ1MgoKIyMjIyBFeHByZXNzaW9uIGRhdGEKCmBgYHtyfQpncy5maWxlIDwtIGZpbGUucGF0aCgiZGF0YSIsICJleHByZXNzaW9uX2RhdGEiLCAKICAgICAgICAgICAgICAgICAgICAgIkUtTVRBQi0yNDUyX2h1Z2VuZTExc3RfU0NBTmZhc3Rfd2l0aF9HZW5lU3ltYm9sLnBjbCIpCmV4cHJzLmRmIDwtcmVhZHI6OnJlYWRfdHN2KGdzLmZpbGUpCiMgZHJvcCBnZW5lIGlkZW50aWZpZXJzCmV4cHJzLm1hdCA8LSBhcy5tYXRyaXgoZXhwcnMuZGZbLCAzOm5jb2woZXhwcnMuZGYpXSkKcm93bmFtZXMoZXhwcnMubWF0KSA8LSBleHBycy5kZiRHZW5lU3ltYm9sCnJtKGV4cHJzLmRmKQpgYGAKCiMjIyMgQiBtYXRyaXgKCkZyb20gcmVjb3VudDIgbW9kZWwKCmBgYHtyfQppc28uYi5maWxlIDwtIGZpbGUucGF0aCgicmVzdWx0cyIsICIwMyIsIAogICAgICAgICAgICAgICAgICAgICAgICAiRS1NVEFCLTI0NTJfQl9tYXRyaXhfcmVjb3VudDJfbW9kZWwudHh0IikKaXNvLmIubWF0cml4IDwtIHJlYWQuZGVsaW0oaXNvLmIuZmlsZSkKYGBgCgojIyMgcmVjb3VudDIKCiMjIyMgUExJRVIgbW9kZWwKCmBgYHtyfQpwbGllci5yZXN1bHRzIDwtIHJlYWRSRFMoZmlsZS5wYXRoKCJkYXRhIiwgInJlY291bnQyX1BMSUVSX2RhdGEiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicmVjb3VudF9QTElFUl9tb2RlbC5SRFMiKSkKYGBgCgojIyMjIElucHV0IGV4cHJlc3Npb24gZGF0YQoKYGBge3J9CiMgZGF0YSB0aGF0IHdhcyBwcmVwcGVkIGZvciB1c2Ugd2l0aCBQTElFUgpyZWNvdW50Lmxpc3QgPC0gcmVhZFJEUyhmaWxlLnBhdGgoImRhdGEiLCAicmVjb3VudDJfUExJRVJfZGF0YSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInJlY291bnRfZGF0YV9wcmVwX1BMSUVSLlJEUyIpKQojIGlucHV0IGV4cHJlc3Npb24gZGF0YSBmcm9tIGludGVybWVkaWF0ZSBmaWxlCnJlY291bnQuaW5wdXQuZXhwcnMgPC0gcmVjb3VudC5saXN0JHJwa20uY20Kcm0ocmVjb3VudC5saXN0KQpgYGAKCiMjIyMgRXZhbHVhdGlvbiBvZiByZWNvbnN0cnVjdGlvbgoKU3BlYXJtYW4gY29ycmVsYXRpb24sIE1BU0UKCmBgYHtyfQpldmFsLmZpbGUgPC0gZmlsZS5wYXRoKCJyZXN1bHRzIiwgIjAyIiwgCiAgICAgICAgICAgICAgICAgICAgICAgInJlY291bnQyX3JlY291bnQyX21vZGVsX3JlY29uX2V2YWxfZGYudHN2IikKcmVjb3VudC5ldmFsLmRmIDwtIHJlYWRyOjpyZWFkX3RzdihldmFsLmZpbGUpCnJlY291bnQuZXZhbC5kZiREYXRhc2V0IDwtIHJlcCgicmVjb3VudDIiLCBucm93KHJlY291bnQuZXZhbC5kZikpCmBgYAoKSWRlbnRpZnkgdGhlIHNhbXBsZXMgdGhhdCBiZWxvbmcgdG8gYFNSUDA0NTUwMGAsIHdlJ2xsIGR1cGxpY2F0ZSB0aGVzZSB2YWx1ZXMKaW4gYHJlY291bnQuZXZhbC5kZmAgc28gd2UgY2FuIHBsb3QgdGhlbSBzZXBhcmF0ZWx5LgoKYGBge3J9CnNycC5kZiA8LSByZWNvdW50LmV2YWwuZGYgJT4lCiAgICAgICAgICAgIGRwbHlyOjpmaWx0ZXIoZ3JlcGwoIlNSUDA0NTUwMCIsIHJlY291bnQuZXZhbC5kZiRTYW1wbGUpKSAlPiUKICAgICAgICAgICAgZHBseXI6Om11dGF0ZShEYXRhc2V0ID0gIlNSUDA0NTUwMCIpCnJlY291bnQuZXZhbC5kZiA8LSBkcGx5cjo6YmluZF9yb3dzKHJlY291bnQuZXZhbC5kZiwgc3JwLmRmKQpgYGAKCiMjIFJlY29uc3RydWN0aW9uIG9mIEUtTVRBQi0yNDUyIHdpdGggYWxsIExWcwoKYGBge3J9CiMgcmVjb3VudDIgbW9kZWwgWiBtYXRyaXgKei5tYXRyaXggPC0gcGxpZXIucmVzdWx0cyRaCmBgYAoKIyMjIFJlY29uc3RydWN0aW9uIGFuZCBldmFsdWF0aW9uCgpXZSBuZWVkIHRvIG9idGFpbiBhIChyb3ctbm9ybWFsaXplZCkgZ2VuZSBleHByZXNzaW9uIG1hdHJpeCBhcyB0aGUgInRydWUiCmV4cHJlc3Npb24gbWF0cml4LiAKV2UgdXNlIHRoaXMgZm9yIGV2YWx1YXRpb24gd2l0aCBTcGVhcm1hbiBhbmQgTUFTRSBhcyBpbiBwcmV2aW91cyBub3RlYm9va3MuCldlJ2xsIHVzZSB0aGlzIGZvciB0aGUgb3RoZXIgZXZhbHVhdGlvbnMgKGUuZy4sIHBhdGh3YXktYXNzb2NpYXRlZCBMVnMgb25seSkgYXMgCndlbGwuCgpgYGB7cn0KaXNvLm9yZC5yb3dub3JtIDwtIEdldE9yZGVyZWRSb3dOb3JtKGV4cHJzLm1hdCA9IGV4cHJzLm1hdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBsaWVyLm1vZGVsID0gcGxpZXIucmVzdWx0cykgICNyZWNvdW50MgpgYGAKCmBgYHtyfQppc28ucmVjb24uYWxsLmRmIDwtIFJlY29uc3RydWN0aW9uRXZhbFdyYXBwZXIoei5tYXQgPSBhcy5tYXRyaXgoei5tYXRyaXgpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYi5tYXQgPSBhcy5tYXRyaXgoaXNvLmIubWF0cml4KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlucHV0LmV4cHJzID0gaXNvLm9yZC5yb3dub3JtLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbHYudHlwZSA9IHBhc3RlKCJBbGwsIG4gPSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5jb2woei5tYXRyaXgpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGFzZXQubmFtZSA9ICJFLU1UQUItMjQ1MiIpCmBgYAoKYGBge3J9CmhlYWQoaXNvLnJlY29uLmFsbC5kZikKYGBgCiMjIyBQbG90dGluZyAKCkFkZCB0aGUgaXNvbGF0ZWQgY2VsbCB0eXBlIHJlY29uc3RydWN0aW9uIGV2YWx1YXRpb24gbWVhc3VyZXMgdG8gdGhlIHJlY291bnQKZGF0YS5mcmFtZQoKYGBge3J9CmV2YWwuZGYgPC0gZHBseXI6OmJpbmRfcm93cyhyZWNvdW50LmV2YWwuZGYsIGlzby5yZWNvbi5hbGwuZGYpCnJtKGlzby5yZWNvbi5hbGwuZGYpCmBgYAojIyMjIENvcnJlbGF0aW9uCmBgYHtyfQpkcGx5cjo6ZmlsdGVyKGV2YWwuZGYsIGBMVnMgdXNlZCBpbiByZWNvbnN0cnVjdGlvbmAgPT0gCiAgICAgICAgICAgICAgICBwYXN0ZSgiQWxsLCBuID0iLCBuY29sKHoubWF0cml4KSkpICU+JQogIGdncGxvdDI6OmdncGxvdChnZ3Bsb3QyOjphZXMoeCA9IGBTcGVhcm1hbiBjb3JyZWxhdGlvbmAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cCA9IERhdGFzZXQsIGZpbGwgPSBEYXRhc2V0KSkgKwogIGdncGxvdDI6Omdlb21fZGVuc2l0eShhbHBoYSA9IDAuMykgKwogIGdncGxvdDI6OnRoZW1lX2J3KCkgKwogIGdncGxvdDI6OnNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIndoaXRlIiwgImdyYXk1MCIsICJibGFjayIpKSArCiAgZ2dwbG90Mjo6Z2d0aXRsZShwYXN0ZSgiQWxsLCBuID0iLCBuY29sKHoubWF0cml4KSkpCmBgYAoKIyMjIyBNQVNFCmBgYHtyfQpkcGx5cjo6ZmlsdGVyKGV2YWwuZGYsIGBMVnMgdXNlZCBpbiByZWNvbnN0cnVjdGlvbmAgPT0gCiAgICAgICAgICAgICAgICBwYXN0ZSgiQWxsLCBuID0iLCBuY29sKHoubWF0cml4KSkpICU+JQogIGdncGxvdDI6OmdncGxvdChnZ3Bsb3QyOjphZXMoeCA9IE1BU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cCA9IERhdGFzZXQsIGZpbGwgPSBEYXRhc2V0KSkgKwogIGdncGxvdDI6Omdlb21fZGVuc2l0eShhbHBoYSA9IDAuMykgKwogIGdncGxvdDI6OnRoZW1lX2J3KCkgKwogIGdncGxvdDI6OnNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIndoaXRlIiwgImdyYXk1MCIsICJibGFjayIpKSArCiAgZ2dwbG90Mjo6Z2d0aXRsZShwYXN0ZSgiQWxsLCBuID0iLCBuY29sKHoubWF0cml4KSkpCmBgYAoKIyMjIFN1bW1hcnkKClRoZSBpc29sYXRlZCBpbW11bmUgY2VsbCBzYW1wbGVzIChgU1JQMDQ1NTAwYCkgYXJlIGFtb25nIHNvbWUgb2YgdGhlIGJlc3QgCnJlY29uc3RydWN0ZWQgKGhpZ2ggY29ycmVsYXRpb24sIGxvdyBlcnJvcikgaW4gdGhlIHJlY291bnQyIGNvbXBlbmRpdW0uCmBFLU1UQUItMjQ1MmAsIHRoZSBtaWNyb2FycmF5IGRhdGFzZXQsIGlzIHJlY29uc3RydWN0ZWQgcmVsYXRpdmVseSBwb29ybHkuIApUaGlzIGlzIHVuc3VycHJpc2luZywgYXMgaXQgd2FzIG5vdCBpbmNsdWRlZCBpbiB0cmFpbmluZyBhbmQgaXMgZnJvbSBhIGRpZmZlcmVudAp0ZWNobm9sb2d5LgpXZSdsbCBsb29rIG5leHQgYXQgcmVjb25zdHJ1Y3Rpb24gdXNpbmcgX3N1YnNldHNfIG9mIHRoZSByZWNvdW50MiBQTElFUiBtb2RlbApMVnMuIApTcGVjaWZpY2FsbHksIHdlIGh5cG90aGVzaXplIHRoYXQgcmVjb25zdHJ1Y3Rpb24gdXNpbmcgb25seSBwYXRod2F5LWFzc29jaWF0ZWQKTFZzIChlLmcuLCB0aG9zZSBMVnMgcm9vdGVkIGluIGJpb2xvZ2ljYWwgc2lnbmFsKSBtYXkgbWFrZSB0aGUgbWVhc3VyZXMgb2YKcmVjb25zdHJ1Y3Rpb24gcGVyZm9ybWFuY2UgLS0gY29ycmVsYXRpb24gYW5kIGVycm9yIC0tIG1vcmUgc2ltaWxhciBiZXR3ZWVuCnRoZSB0d28gaXNvbGF0ZWQgaW1tdW5lIGNlbGwgZGF0YXNldHMuCklmIHRoaXMgaXMgdGhlIGNhc2UsIGl0IHN1cHBvcnRzIHRoZSBpZGVhIHRoYXQgdGhlIFBMSUVSLWxlYXJuZWQgTFZzIHRoYXQgYXJlIApub3QgYXNzb2NpYXRlZCB3aXRoIHBhdGh3YXlzIGFyZSwgYXQgbGVhc3QgaW4gcGFydCwgY2FwdHVyaW5nIHZhcmlhdGlvbiB0aGF0CmNhbiBiZSBhdHRyaWJ1dGVkIHRvIHRlY2huaWNhbCBmYWN0b3JzLgpXZSB3b3VsZCBleHBlY3QgdGhhdCB0aGUgdGVjaG5pY2FsIHNpZ25hbHMgaW4gcmVjb3VudDIgd291bGQgYmUgZGlzdGluY3QgCihlLmcuLCBhbiBMViBhc3NvY2lhdGVkIHdpdGggbGlicmFyeSBwcmVwIHN0cmF0ZWd5KSBmcm9tIHdoYXQncyBpbiAKYEUtTVRBQi0yNDUyYCBhbmQsIHRoZXJlZm9yZSwgdGhhdCBpbmNsdWRpbmcgdGhpcyBpbmZvcm1hdGlvbiBtYXkgbmVnYXRpdmVseSAKaW1wYWN0IHJlY29uc3RydWN0aW9uIG9mIHRoaXMgZGF0YXNldC4KCiMjIFJlY29uc3RydWN0aW9uIG9mIEUtTVRBQi0yNDUyIHdpdGggcGF0aHdheS1hc3NvY2lhdGVkIExWcyBvbmx5CgpgYGB7cn0KIyBHZXQgdGhlIHNpZ25pZmljYW50IExWcyAoZnJvbSByZWNvdW50MiBtb2RlbCkgdXNpbmcgMC4wNSBhcyB0aGUgRkRSIGN1dG9mZgpwbGllci5zdW1tYXJ5IDwtIHBsaWVyLnJlc3VsdHMkc3VtbWFyeQpzaWcuc3VtbWFyeSA8LSBwbGllci5zdW1tYXJ5ICU+JQogICAgICAgICAgICAgICAgICBkcGx5cjo6ZmlsdGVyKEZEUiA8IDAuMDUpCnNpZy5sdnMgPC0gdW5pcXVlKHNpZy5zdW1tYXJ5JGBMViBpbmRleGApCmBgYAoKYGBge3J9CiMgZHJvcCBjb2x1bW5zIChMVnMpIGZyb20gWiB0aGF0IGFyZSBub3Qgc2lnbmlmaWNhbnRseSBhc3NvY2lhdGVkIHdpdGggcHJpb3IgCiMgaW5mbwpzaWcuei5tYXQgPC0gei5tYXRyaXhbLCBhcy5pbnRlZ2VyKHNpZy5sdnMpXQojIGRyb3Agcm93cyAoTFZzKSBmcm9tIEUtTVRBQi0yNDUyIEIgdGhhdCBhcmUgbm90IHNpZ25pZmljYW50bHkgYXNzb2NpYXRlZCB3aXRoIAojIHByaW9yIGluZm8KaXNvLnNpZy5iLm1hdCA8LSBhcy5tYXRyaXgoaXNvLmIubWF0cml4W2FzLmludGVnZXIoc2lnLmx2cyksIF0pCmBgYAoKIyMjIFJlY29uc3RydWN0aW9uIGFuZCBldmFsdWF0aW9uCgpgYGB7cn0KcGF0aC5sdi50eXBlIDwtIHBhc3RlKCJQYXRod2F5LWFzc29jaWF0ZWQsIG4gPSIsIG5jb2woc2lnLnoubWF0KSkKaXNvLnJlY29uLnNpZy5kZiA8LSBSZWNvbnN0cnVjdGlvbkV2YWxXcmFwcGVyKHoubWF0ID0gYXMubWF0cml4KHNpZy56Lm1hdCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBiLm1hdCA9IGlzby5zaWcuYi5tYXQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbnB1dC5leHBycyA9IGlzby5vcmQucm93bm9ybSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGFzZXQubmFtZSA9ICJFLU1UQUItMjQ1MiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsdi50eXBlID0gcGF0aC5sdi50eXBlKQpoZWFkKGlzby5yZWNvbi5zaWcuZGYpCmBgYApBZGQgdG8gcmVzdCBvZiBldmFsdWF0aW9ucwoKYGBge3J9CmV2YWwuZGYgPC0gZHBseXI6OmJpbmRfcm93cyhldmFsLmRmLCBpc28ucmVjb24uc2lnLmRmKQpybShpc28ucmVjb24uc2lnLmRmKQpgYGAKCiMjIFJlY29uc3RydWN0aW9uIHdpdGggTFZzIHRoYXQgYXJlIG5vdCBhc3NvY2lhdGVkIHdpdGggcGF0aHdheXMKCkhlcmUsIHdlJ2xsIGhhdmUgdG8gcmVjb25zdHJ1Y3QgdGhlIHJlY291bnQyIGRhdGEgd2l0aCB0aGUgTFZzIHRoYXQgYXJlIG5vdApzaWduaWZpY2FudGx5IGFzc29jaWF0ZWQgd2l0aCBwYXRod2F5cywgYXMgd2UgZGlkIG5vdCBwZXJmb3JtIHRoaXMgYW5hbHlzaXMKaW4gYDAyLXJlY291bnQyX1BMSUVSX2V4cGxvcmF0aW9uYC4KCmBgYHtyfQojIHNvbWUgTFZzIGRvbid0IGV2ZW4gbWFrZSBpdCB0byB0aGUgc3VtbWFyeSBkYXRhLmZyYW1lIGZyb20gdGhlIFBMSUVSIG1vZGVsCiMgdGhlIExWcyB3ZSB3YW50IGhlcmUgYXJlICphbGwqIHRoZSBMVnMgdGhhdCBkbyBub3QgaGF2ZSBhdCBsZWFzdCBvbmUgcGF0aHdheSAKIyBzaWduaWZpY2FudGx5IGFzc29jaWF0ZWQgd2l0aCB0aGVtCmFsbCgxOm5yb3cocGxpZXIucmVzdWx0cyRCKSAlaW4lIHVuaXF1ZShwbGllci5zdW1tYXJ5JGBMViBpbmRleGApKQpgYGAKCmBgYHtyfQojIG5vbi1zaWduaWZpY2FudCBMVnMsIHRoZW4sIGFyZSBhbGwgTFZzIHRoYXQgYXJlIG5vdCBpbiB0aGUgbGlzdCBvZiAKIyBwYXRod2F5LWFzc29jaWF0ZWQgTFZzCm5vbi5sdnMgPC0gc2V0ZGlmZigxOm5yb3cocGxpZXIucmVzdWx0cyRCKSwgYXMuaW50ZWdlcihzaWcubHZzKSkKYGBgCmBgYHtyfQojIHogbWF0cml4IGZvciBqdXN0IHRoZXNlIExWcwpub24uei5tYXQgPC0gei5tYXRyaXhbLCBhcy5pbnRlZ2VyKG5vbi5sdnMpXQpgYGAKIyMjIHJlY291bnQyCgpgYGB7cn0KIyBnZXQgQiBtYXRyaXgKbm9uLmIubWF0IDwtIGFzLm1hdHJpeChwbGllci5yZXN1bHRzJEJbYXMuaW50ZWdlcihub24ubHZzKSwgXSkKIyByZWNvbnN0cnVjdGlvbiAmIGV2YWx1YXRpb24gCm5vbi5sdi50eXBlIDwtIHBhc3RlKCJMVnMgbm90IGFzc29jaWF0ZWQgd2l0aCBwYXRod2F5LCBuID0iLCBsZW5ndGgobm9uLmx2cykpCnJlY291bnQucmVjb24ubm9uLmRmIDwtIAogIFJlY29uc3RydWN0aW9uRXZhbFdyYXBwZXIoei5tYXQgPSBhcy5tYXRyaXgobm9uLnoubWF0KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGIubWF0ID0gbm9uLmIubWF0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5wdXQuZXhwcnMgPSByZWNvdW50LmlucHV0LmV4cHJzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgbHYudHlwZSA9IG5vbi5sdi50eXBlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YXNldC5uYW1lID0gInJlY291bnQyIikKaGVhZChyZWNvdW50LnJlY29uLm5vbi5kZikKYGBgCgojIyMjIFNSUDA0NTUwMAoKYGBge3J9CnNycC5ub24uZGYgPC0gcmVjb3VudC5yZWNvbi5ub24uZGYgJT4lCiAgICAgICAgICAgICAgICBkcGx5cjo6ZmlsdGVyKGdyZXBsKCJTUlAwNDU1MDAiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVjb3VudC5yZWNvbi5ub24uZGYkU2FtcGxlKSkgJT4lCiAgICAgICAgICAgICAgICBkcGx5cjo6bXV0YXRlKERhdGFzZXQgPSAiU1JQMDQ1NTAwIikKcmVjb3VudC5yZWNvbi5ub24uZGYgPC0gZHBseXI6OmJpbmRfcm93cyhyZWNvdW50LnJlY29uLm5vbi5kZiwgc3JwLm5vbi5kZikKYGBgCgpgYGB7cn0KZXZhbC5kZiA8LSBkcGx5cjo6YmluZF9yb3dzKGV2YWwuZGYsIHJlY291bnQucmVjb24ubm9uLmRmKQpybShyZWNvdW50LnJlY29uLm5vbi5kZiwgc3JwLm5vbi5kZikKYGBgCgojIyMgRS1NVEFCLTI0NTIKCmBgYHtyfQojIGdldCBiIG1hdHJpeAppc28ubm9uLmIubWF0IDwtIGFzLm1hdHJpeChpc28uYi5tYXRyaXhbYXMuaW50ZWdlcihub24ubHZzKSwgXSkKIyByZWNvbnN0cnVjdGlvbiAmIGV2YWx1YXRpb24gCmlzby5yZWNvbi5ub24uZGYgPC0gCiAgUmVjb25zdHJ1Y3Rpb25FdmFsV3JhcHBlcih6Lm1hdCA9IGFzLm1hdHJpeChub24uei5tYXQpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgYi5tYXQgPSBpc28ubm9uLmIubWF0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5wdXQuZXhwcnMgPSBpc28ub3JkLnJvd25vcm0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsdi50eXBlID0gbm9uLmx2LnR5cGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhc2V0Lm5hbWUgPSAiRS1NVEFCLTI0NTIiKQpoZWFkKGlzby5yZWNvbi5ub24uZGYpCmBgYAoKYGBge3J9CmV2YWwuZGYgPC0gZHBseXI6OmJpbmRfcm93cyhldmFsLmRmLCBpc28ucmVjb24ubm9uLmRmKQpybShpc28ucmVjb24ubm9uLmRmKQpgYGAKCiMjIFBsb3R0aW5nCgpQbG90IGFsbCB0aHJlZSBjb25kaXRpb25zIG9uIHRoZSBzYW1lIHBsb3QgZm9yIGVhc3kgY29tcGFyaXNvbgoKIyMjIERlbnNpdHkKCiMjIyMgQ29ycmVsYXRpb24gCmBgYHtyfQpnZ3Bsb3QyOjpnZ3Bsb3QoZXZhbC5kZiwKICBnZ3Bsb3QyOjphZXMoeCA9IGBTcGVhcm1hbiBjb3JyZWxhdGlvbmAsIGdyb3VwID0gRGF0YXNldCwgZmlsbCA9IERhdGFzZXQpKSArCiAgZ2dwbG90Mjo6ZmFjZXRfd3JhcCh+IGBMVnMgdXNlZCBpbiByZWNvbnN0cnVjdGlvbmAsIG5jb2wgPSAxKSArCiAgZ2dwbG90Mjo6Z2VvbV9kZW5zaXR5KGFscGhhID0gMC4zKSArCiAgZ2dwbG90Mjo6dGhlbWVfYncoKSArCiAgZ2dwbG90Mjo6c2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygid2hpdGUiLCAiZ3JheTUwIiwgImJsYWNrIikpCmBgYAoKYGBge3J9CnBsb3QuZmlsZSA8LSBmaWxlLnBhdGgocGxvdC5kaXIsIAogICAgICAgICAgICAgICAgICAgICAgICJyZWNvdW50Ml9tb2RlbF9pc29sYXRlZF9jZWxsX3JlY29uX2NvcnJlbGF0aW9uLnBkZiIpCmdncGxvdDI6Omdnc2F2ZShmaWxlbmFtZSA9IHBsb3QuZmlsZSwgcGxvdCA9IGdncGxvdDI6Omxhc3RfcGxvdCgpLAogICAgICAgICAgICAgICAgaGVpZ2h0ID0gMTEsIHdpZHRoID0gOC41KQpgYGAKCiMjIyMgRXJyb3IKYGBge3J9CmdncGxvdDI6OmdncGxvdChldmFsLmRmLAogIGdncGxvdDI6OmFlcyh4ID0gTUFTRSwgZ3JvdXAgPSBEYXRhc2V0LCBmaWxsID0gRGF0YXNldCkpICsKICBnZ3Bsb3QyOjpmYWNldF93cmFwKH4gYExWcyB1c2VkIGluIHJlY29uc3RydWN0aW9uYCwgbmNvbCA9IDEpICsKICBnZ3Bsb3QyOjpnZW9tX2RlbnNpdHkoYWxwaGEgPSAwLjMpICsKICBnZ3Bsb3QyOjp0aGVtZV9idygpICsKICBnZ3Bsb3QyOjpzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJ3aGl0ZSIsICJncmF5NTAiLCAiYmxhY2siKSkKYGBgCgpgYGB7cn0KcGxvdC5maWxlIDwtIGZpbGUucGF0aChwbG90LmRpciwgCiAgICAgICAgICAgICAgICAgICAgICAgInJlY291bnQyX21vZGVsX2lzb2xhdGVkX2NlbGxfcmVjb25fZXJyb3IucGRmIikKZ2dwbG90Mjo6Z2dzYXZlKGZpbGVuYW1lID0gcGxvdC5maWxlLCBwbG90ID0gZ2dwbG90Mjo6bGFzdF9wbG90KCksCiAgICAgICAgICAgICAgICBoZWlnaHQgPSAxMSwgd2lkdGggPSA4LjUpCmBgYAoKIyMjIEUtTVRBQi0yNDUyIEJveHBsb3RzCgpgYGB7cn0KYXJyYXkuZXZhbC5kZiA8LSBkcGx5cjo6ZmlsdGVyKGV2YWwuZGYsIERhdGFzZXQgPT0gIkUtTVRBQi0yNDUyIikKYGBgCgojIyMjIENvcnJlbGF0aW9uCmBgYHtyfQpnZ3Bsb3QyOjpnZ3Bsb3QoYXJyYXkuZXZhbC5kZiwgCiAgICAgICAgICAgICAgICBnZ3Bsb3QyOjphZXMoeCA9IGBMVnMgdXNlZCBpbiByZWNvbnN0cnVjdGlvbmAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IGBTcGVhcm1hbiBjb3JyZWxhdGlvbmApKSArCiAgZ2dwbG90Mjo6Z2VvbV9ib3hwbG90KCkgKwogIGdncGxvdDI6OnRoZW1lX2J3KCkgKwogIGdncGxvdDI6OnNjYWxlX3hfZGlzY3JldGUobGFiZWxzID0gYygiQWxsIiwgIk5vdCBwYXRod2F5LWFzc29jaWF0ZWQiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlBhdGh3YXktYXNzb2NpYXRlZCIpKSArCiAgZ2dwbG90Mjo6Z2d0aXRsZSgiRS1NVEFCLTI0NTIgcmVjb25zdHJ1Y3Rpb24gd2l0aCByZWNvdW50MiBtb2RlbCIpCmBgYApgYGB7cn0KcGxvdC5maWxlIDwtIGZpbGUucGF0aChwbG90LmRpciwKICAgICAgICAgICAgICAgICAgICAgICAiRS1NVEFCLTI0NTJfcmVjb25zdHJ1Y3Rpb25fY29ycl9yZWNvdW50Ml9tb2RlbC5wZGYiKQpnZ3Bsb3QyOjpnZ3NhdmUocGxvdC5maWxlLCBwbG90ID0gZ2dwbG90Mjo6bGFzdF9wbG90KCkpCmBgYAoKYGBge3J9CnBhaXJ3aXNlLnQudGVzdCh4ID0gYXJyYXkuZXZhbC5kZiRgU3BlYXJtYW4gY29ycmVsYXRpb25gLAogICAgICAgICAgICAgICAgZyA9IGFycmF5LmV2YWwuZGYkYExWcyB1c2VkIGluIHJlY29uc3RydWN0aW9uYCwKICAgICAgICAgICAgICAgIHAuYWRqdXN0Lm1ldGhvZCA9ICJib25mZXJyb25pIikKYGBgCgojIyMjIEVycm9yCmBgYHtyfQpnZ3Bsb3QyOjpnZ3Bsb3QoYXJyYXkuZXZhbC5kZiwgCiAgICAgICAgICAgICAgICBnZ3Bsb3QyOjphZXMoeCA9IGBMVnMgdXNlZCBpbiByZWNvbnN0cnVjdGlvbmAsIHkgPSBNQVNFKSkgKwogIGdncGxvdDI6Omdlb21fYm94cGxvdCgpICsKICBnZ3Bsb3QyOjp0aGVtZV9idygpICsKICBnZ3Bsb3QyOjpzY2FsZV94X2Rpc2NyZXRlKGxhYmVscyA9IGMoIkFsbCIsICJOb3QgcGF0aHdheS1hc3NvY2lhdGVkIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJQYXRod2F5LWFzc29jaWF0ZWQiKSkgKwogIGdncGxvdDI6OmdndGl0bGUoIkUtTVRBQi0yNDUyIHJlY29uc3RydWN0aW9uIHdpdGggcmVjb3VudDIgbW9kZWwiKQpgYGAKYGBge3J9CnBsb3QuZmlsZSA8LSBmaWxlLnBhdGgocGxvdC5kaXIsCiAgICAgICAgICAgICAgICAgICAgICAgIkUtTVRBQi0yNDUyX3JlY29uc3RydWN0aW9uX2Vycm9yX3JlY291bnQyX21vZGVsLnBkZiIpCmdncGxvdDI6Omdnc2F2ZShwbG90LmZpbGUsIHBsb3QgPSBnZ3Bsb3QyOjpsYXN0X3Bsb3QoKSkKYGBgCmBgYHtyfQpwYWlyd2lzZS50LnRlc3QoeCA9IGFycmF5LmV2YWwuZGYkTUFTRSwKICAgICAgICAgICAgICAgIGcgPSBhcnJheS5ldmFsLmRmJGBMVnMgdXNlZCBpbiByZWNvbnN0cnVjdGlvbmAsCiAgICAgICAgICAgICAgICBwLmFkanVzdC5tZXRob2QgPSAiYm9uZmVycm9uaSIpCmBgYAoKIyMgU3VtbWFyeQoKUmVjb25zdHJ1Y3Rpb24gb2YgYSB0ZXN0IGRhdGFzZXQsIGBFLU1UQUItMjQ1MmAsIHVzaW5nIG9ubHkgUExJRVIgbGF0ZW50IAp2YXJpYWJsZXMgdGhhdCBzaWduaWZpY2FudGx5IGFzc29jaWF0ZWQgd2l0aCBhIHBhdGh3YXkgb3IgZ2VuZSBzZXQgaXMgCmJldHRlciBpbiB0ZXJtcyBvZiBib3RoIE1BU0UgYW5kIFNwZWFybWFuIGNvcnJlbGF0aW9uIHRoYW4gd2hlbiB1c2luZyBvbmx5CmxhdGVudCB2YXJpYWJsZXMgdGhhdCBhcmUgbm90IGFzc29jaWF0ZWQgd2l0aCBhbnkgcHJpb3IgaW5mb3JtYXRpb24uClRoaXMgaXMgZGVzcGl0ZSB0aGUgZmFjdCB0aGF0IHRoZXJlIGFyZSBtb3JlIG5vbi1zaWduaWZpY2FudCBMVnMgKGBuID0gNzg4YCB2cy4KYG4gPSAxOTlgKS4KSG93ZXZlciwgbmVpdGhlciBvZiB0aGVzZSBzdWJzZXRzIG9mIExWcyBvdXRwZXJmb3JtcyB1c2luZyBhbGwgTFZzCmFuZCB0aGUgcGVyZm9ybWFuY2UgZ2FwIGJldHdlZW4gcmVjb25zdHJ1Y3RpbmcgYEUtTVRBQi0yNDUyYCBhbmQgYFNSUDA0NTUwMGAgCih0aGUgc29ydGVkIGltbXVuZSBjZWxsIGRhdGFzZXQgaW5jbHVkZWQgaW4gcmVjb3VudDIpIGlzIG5vdCBjb25zaWRlcmFibHkgCnJlZHVjZWQgd2hlbiB1c2luZyBvbmx5IHBhdGh3YXktYXNzb2NpYXRlZCBMVnMsIHdoaWNoIHJlZnV0ZXMgb3VyIGluaXRpYWwKaHlwb3RoZXNpcy4KSXQncyBhbHNvIGludGVyZXN0aW5nIHRoYXQsIHdoZW4gcmVjb25zdHJ1Y3RpbmcgdXNpbmcgb25seSBub24tc2lnbmlmaWNhbnQgTFZzLCAKdGhlIHByZS0gYW5kIHBvc3QtcmVjb25zdHJ1Y3Rpb24gY29ycmVsYXRpb24gdmFsdWVzIGFyZSBtdWNoIG1vcmUgc2ltaWxhciAKYmV0d2VlbiBgRS1NVEFCLTI0NTJgIGFuZCBgU1JQMDQ1NTAwYC4KVGhpcyBzdWdnZXN0cyB0aGF0IHRoZSBwYXRod2F5LWFzc29jaWF0ZWQgTFZzIGFyZSBpbXBvcnRhbnQgZm9yIGFjY3VyYXRlCnJlY29uc3RydWN0aW9uIG9mIHRoZSBgU1JQMDQ1NTAwYCBkYXRhc2V0LgoKX0l0J3Mgd29ydGggbm90aW5nIHRoYXQgdGhlc2Ugc29ydGVkIGltbXVuZSBjZWxsIGRhdGFzZXRzIGFyZSBmcm9tIApwYXRpZW50cyB3aXRoIGRpZmZlcmVudCBkaWFnbm9zZXMgYW5kIGNvbnRhaW4gZGlmZmVyZW50IGNlbGwgdHlwZSBwb3B1bGF0aW9ucyAKKGluIGFkZGl0aW9uIHRvIGRpZmZlcmVudCB0ZWNobm9sb2dpZXMpIHRoYXQgbWF5IGJlIG1vcmUgb3IgbGVzcyAKd2VsbC1yZXByZXNlbnRlZCBpbiB0aGUgcmVjb3VudDIgY29tcGVuZGl1bS4KV2UgZXhwZWN0IHRoYXQgdGhlc2UgZGlmZmVyZW5jZXMgY29udHJpYnV0ZSB0byB0aGUgcmVzdWx0cy4gCkZ1dHVyZSBhbmFseXNlcyBtYXkgZm9jdXMgb24gYSBtaWNyb2FycmF5IGRhdGFzZXQgbW9yZSBjb21wYXJhYmxlIHRvIGBTUlAwNDU1MDBgCmluIHRoZXNlIHdheXMuXwo=