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"))

Input expression data

# data that was prepped for use with PLIER
recount.list <- readRDS(file.path("data", "recount2_PLIER_data", 
                                  "recount_data_prep_PLIER.RDS"))
# input expression data from intermediate file
recount.input.exprs <- recount.list$rpkm.cm
rm(recount.list)

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=