J. Taroni 2018

In 06-sle-wb_cell_type, we examined how LVs from the SLE WB PLIER model (results/05/SLE-WB_PLIER_model.RDS) compared to known patterns in the Banchereau, et al. data set that was included in the SLE WB compendium (and therefore, training).

Specifically, we asked:

  • Are there neutrophil-associated LVs that are correlated with neutrophil counts?
  • Are there plasma cell-associated LVs that demonstrate differential expression between disease activity groups (specifically, higher values in more severe disease)?

The answer to both of these questions is yes. This demonstrated the utility of PLIER for extracting cell type patterns from heterogeneous (at least in terms of patient population and platform) gene expression data from a single tissue (whole blood) that had been assembled into a compendium (greenelab/rheum-plier-data/sle-wb).

SLE is not a rare condition – though the molecular heterogeneity and multi-tissue nature of the disease warrant sophisticated approaches to the analysis of transcriptomic data. (This SLE WB compendium is comprised of 1640 samples and is incomplete; we picked 7 data sets based on the platform used and the availability of raw data.) Thus, we can use data-intensive approaches (e.g., PLIER) that are inappropriate for smaller data sets and, by extension, rare or understudied diseases.

Using a “shovel-ready” data set like recount2 to train a PLIER model (see greenelab/rheum-plier-data/recount2) and applying this model to smaller data sets may “get around” the data-intensive requirement, but more investigation is warranted.

In this notebook, we examine whether we can use the recount2 PLIER model (data/recount2_PLIER_data/recount_PLIER_model.RDS) to analyze neutrophil and plasma cell patterns in the Banchereau, et al. data. If the recount2 model performance is similar to that of the SLE model (examined in 06-sle-wb_cell_type), that is evidence supporting the validity of this transfer learning approach.

Functions and directory set up

`%>%` <- dplyr::`%>%`
Warning message:
In scan(file = file, what = what, sep = sep, quote = quote, dec = dec,  :
  EOF within quoted string
library(PLIER)
Loading required package: RColorBrewer
Loading required package: gplots

Attaching package: ‘gplots’

The following object is masked from ‘package:stats’:

    lowess

Loading required package: pheatmap
Loading required package: glmnet
Loading required package: Matrix
Loading required package: foreach
Loaded glmnet 2.0-13

Loading required package: knitr
Loading required package: rsvd
Loading required package: qvalue
# custom functions
source(file.path("util", "plier_util.R"))
# plot and result directory setup for this notebook
plot.dir <- file.path("plots", "07")
dir.create(plot.dir, recursive = TRUE, showWarnings = FALSE)
results.dir <- file.path("results", "07")
dir.create(results.dir, recursive = TRUE, showWarnings = FALSE)

Load data

recount2 model

recount.plier <- readRDS(file.path("data", "recount2_PLIER_data", 
                                   "recount_PLIER_model.RDS"))

SLE WB expression data

symbol.file <- 
  file.path("data", "expression_data", 
            "SLE_WB_all_microarray_QN_zto_before_with_GeneSymbol.pcl")
sle.exprs.df <- readr::read_tsv(symbol.file, progress = FALSE)
Parsed with column specification:
cols(
  .default = col_double(),
  EntrezID = col_integer(),
  GeneSymbol = col_character()
)
See spec(...) for full column specifications.
# matrix with gene symbol as rownames
exprs.mat <- dplyr::select(sle.exprs.df, -EntrezID)
rownames(exprs.mat) <- exprs.mat$GeneSymbol
Setting row names on a tibble is deprecated.
exprs.mat <- as.matrix(dplyr::select(exprs.mat, -GeneSymbol))

SLE WB cell type LV dfs

From 06-sle-wb_cell_type

neutro.file <- file.path("results", "06",
                         "Banchereau_count_SLE_model_neutrophil_LV.tsv")
sle.neutro.df <- readr::read_tsv(neutro.file)
Parsed with column specification:
cols(
  Sample = col_character(),
  LV2 = col_double(),
  LV27 = col_double(),
  LV34 = col_double(),
  LV87 = col_double(),
  Neutrophil.Count = col_double()
)
plasma.file <- file.path("results", "06", 
                         "Banchereau_DA_group_SLE_model_plasma_cell_LVs.tsv")
sle.plasma.df <- readr::read_tsv(plasma.file)
Parsed with column specification:
cols(
  Sample = col_character(),
  LV52 = col_double(),
  LV136 = col_double(),
  Disease.Activity = col_character()
)

Cell type-associated LVs in the recount2 model

# summary information from the recount2 PLIER model
recount.summary <- recount.plier$summary

Neutrophil

Are there recount2 LVs associated with neutrophil gene sets?

recount.summary %>%
  dplyr::filter(grepl("Neutrophil", pathway), 
                FDR < 0.05)

The following latent variables look most promising: LV524, LV603, LV985 (AUC > 0.75 for both SVM Neutrophils and IRIS_Neutrophil-Resting)

What other gene sets are associated with these LVs?

recount.summary %>%
  dplyr::filter(`LV index` %in% c(524, 603, 985),
                FDR < 0.05) %>%
  dplyr::arrange(`LV index`, desc(AUC))

Some notes on these results:

  • LV524 is also associated with the following monocyte-related gene sets: SVM Monocytes, DMAP_MONO1, and DMAP_MONO2. Again, we would like something that tells us about neutrophils only if at all possible.
  • LV603 is associated with two other gene sets, PID_IL8CXCR2_PATHWAY and SIG_PIP3_SIGNALING_IN_B_LYMPHOCYTES. It’s worth noting that interleukin 8 (IL8) is a neutrophil chemoattractant, and SIG_PIP3_SIGNALING_IN_B_LYMPHOCYTES may just be related to PI3K signaling & chemotaxis in general.
  • LV985 is also associated with SIG_PIP3_SIGNALING_IN_B_LYMPHOCYTES and PID_FCER1PATHWAY. FCER1 refers to Fc-epsilon receptor 1 which is mainly expressed on other granulocytes.

U matrix

PLIER::plotU(plierRes = recount.plier,
             pval.cutoff = 1e-06,
             indexCol = c(524, 603, 985),
             top = 10)

png(file.path(plot.dir, "recount2_neutrophil_Uplot.png"), 
    res = 300, width = 7, height = 7, units = "in")
PLIER::plotU(plierRes = recount.plier,
             pval.cutoff = 1e-06,
             indexCol = c(524, 603, 985),
             top = 10)
dev.off()
null device 
          1 

Plasma cell

Are there LVs associated with plasma cell gene sets in the recount2 model?

recount.summary %>%
  dplyr::filter(grepl("Plasma", pathway), 
                FDR < 0.05)

What other gene sets are associated with LV951?

recount.summary %>%
  dplyr::filter(`LV index` == 951)

KEGG_INTESTINAL_IMMUNE_NETWORK_FOR_IGA_PRODUCTION is not a significant association (FDR = 0.13) There were 2 LVs from the SLE WB PLIER model that were associated with plasma cell gene sets. Those LVs were also associated with other processes (e.g., REACTOME_UNFOLDED_PROTEIN_RESPONSE, KEGG_HEDGEHOG_SIGNALING_PATHWAY)

Transform SLE WB data

sle.recount.b <- GetNewDataB(exprs.mat = exprs.mat,
                             plier.model = recount.plier)
b.file <- file.path(results.dir, "SLE-WB_B_matrix_recount2_model.RDS")
saveRDS(sle.recount.b, file = b.file)

Banchereau, et al. cell type results

Neutrophil

neutro.lv.df <- as.data.frame(cbind(colnames(sle.recount.b), 
                                    t(sle.recount.b[c(524, 603, 985), ])))
colnames(neutro.lv.df) <- c("Sample", "recount2_LV524", "recount2_LV603", 
                            "recount2_LV985")
# join with sle wb results & neutrophil counts
neutro.df <- dplyr::inner_join(sle.neutro.df, neutro.lv.df) %>%
              dplyr::mutate(recount2_LV524 = as.numeric(as.character(recount2_LV524)), 
                            recount2_LV603 = as.numeric(as.character(recount2_LV603)),
                            recount2_LV985 = as.numeric(as.character(recount2_LV985)))
Joining, by = "Sample"
Column `Sample` joining character vector and factor, coercing into character vector
head(neutro.df)
neutro.out.file <- file.path(results.dir, "neutrophil_count_LV_both_models.tsv")
readr::write_tsv(neutro.df, path = neutro.out.file)
# function for scatter plots, given lv (a string indicating the LV of interest)
# make a scatter plot where the LV is the x variable, neutrophil count is the
# y variable & fit a line with geom_smooth(method = "lm")
# also will annotate the plot with the supplied r-squared value (rsq arg)
# where the text is placed is automatically chosen from the x and y values
# needs to be used in this global environment
LVScatter <- function(lv, rsq) {
  y.var <- "Neutrophil.Count"
  
  # calculate where to put the r-squared value
  x.range <- max(neutro.df[, lv]) - min(neutro.df[, lv])
  x.coord <- min(neutro.df[, lv]) + (x.range * 0.15)
  y.range <- max(neutro.df[, y.var]) - min(neutro.df[, y.var])
  y.coord <- max(neutro.df[, y.var]) - (y.range * 0.15)
  
  ggplot2::ggplot(neutro.df, ggplot2::aes_string(x = lv, y = y.var)) +
    ggplot2::geom_point(alpha = 0.7) +
    ggplot2::geom_smooth(method = "lm") +
    ggplot2::theme_bw() +
    ggplot2::labs(y = "Neutrophil Count") +
    ggplot2::theme(legend.position = "none", 
                   text = ggplot2::element_text(size = 15)) +
    ggplot2::annotate("text", x = x.coord, y = y.coord, 
                      label = paste("r-squared =", rsq))
}

LV524

summary(lm(neutro.df$Neutrophil.Count ~ neutro.df$recount2_LV524))

Call:
lm(formula = neutro.df$Neutrophil.Count ~ neutro.df$recount2_LV524)

Residuals:
    Min      1Q  Median      3Q     Max 
-8.8588 -1.1612 -0.2481  0.9283 10.7119 

Coefficients:
                         Estimate Std. Error t value Pr(>|t|)    
(Intercept)               4.12742    0.06649   62.08   <2e-16 ***
neutro.df$recount2_LV524 20.67546    0.97663   21.17   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 1.94 on 851 degrees of freedom
Multiple R-squared:  0.345, Adjusted R-squared:  0.3442 
F-statistic: 448.2 on 1 and 851 DF,  p-value: < 2.2e-16
LVScatter(lv = "recount2_LV524", rsq = 0.34)

LV603

summary(lm(neutro.df$Neutrophil.Count ~ neutro.df$recount2_LV603))

Call:
lm(formula = neutro.df$Neutrophil.Count ~ neutro.df$recount2_LV603)

Residuals:
    Min      1Q  Median      3Q     Max 
-4.1245 -1.1945 -0.2527  0.8522 12.0900 

Coefficients:
                         Estimate Std. Error t value Pr(>|t|)    
(Intercept)               4.00409    0.06612   60.56   <2e-16 ***
neutro.df$recount2_LV603 11.09268    0.50550   21.94   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 1.916 on 851 degrees of freedom
Multiple R-squared:  0.3614,    Adjusted R-squared:  0.3606 
F-statistic: 481.5 on 1 and 851 DF,  p-value: < 2.2e-16
LVScatter(lv = "recount2_LV603", rsq = 0.36)

LV985

summary(lm(neutro.df$Neutrophil.Count ~ neutro.df$recount2_LV985))

Call:
lm(formula = neutro.df$Neutrophil.Count ~ neutro.df$recount2_LV985)

Residuals:
    Min      1Q  Median      3Q     Max 
-4.7147 -1.2945 -0.3547  0.8808 12.6068 

Coefficients:
                         Estimate Std. Error t value Pr(>|t|)    
(Intercept)               4.06785    0.06814   59.70   <2e-16 ***
neutro.df$recount2_LV985 14.77640    0.74475   19.84   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 1.982 on 851 degrees of freedom
Multiple R-squared:  0.3163,    Adjusted R-squared:  0.3155 
F-statistic: 393.7 on 1 and 851 DF,  p-value: < 2.2e-16
LVScatter(lv = "recount2_LV985", rsq = 0.32)

The neutrophil-associated LVs from the recount2 model generally perform as well as the neutrophil-associated LVs from the SLE WB PLIER model.

We’ll plot the results from SLE WB LV87 and recount2 LV603 together because these are both LVs that are not significantly associated with monocyte signatures and have high AUC for the neutrophil gene sets.

sle.plot <- LVScatter("LV87", rsq = 0.29) +
  ggplot2::labs(title = "SLE WB PLIER model") +
  ggplot2::theme(plot.title = ggplot2::element_text(hjust = 0.5, 
                                                    face = "bold"))
recount.plot <- LVScatter("recount2_LV603", rsq = 0.36) +
  ggplot2::labs(title = "recount2 PLIER model") +
  ggplot2::theme(plot.title = ggplot2::element_text(hjust = 0.5, 
                                                    face = "bold"))
pdf(file.path(plot.dir, "Neutrophil_LV_model_comparison.pdf"), width = 14,
    height = 7)
gridExtra::grid.arrange(sle.plot, recount.plot, ncol = 2)
dev.off()
null device 
          1 

Correlation between LV values from different models

summary(lm(neutro.df$LV87 ~ neutro.df$recount2_LV603))

Call:
lm(formula = neutro.df$LV87 ~ neutro.df$recount2_LV603)

Residuals:
     Min       1Q   Median       3Q      Max 
-0.66051 -0.07107  0.01107  0.08593  0.33998 

Coefficients:
                          Estimate Std. Error t value Pr(>|t|)    
(Intercept)              -0.016814   0.004702  -3.576 0.000369 ***
neutro.df$recount2_LV603  2.753708   0.035945  76.610  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.1362 on 851 degrees of freedom
Multiple R-squared:  0.8734,    Adjusted R-squared:  0.8732 
F-statistic:  5869 on 1 and 851 DF,  p-value: < 2.2e-16
ggplot2::ggplot(neutro.df, ggplot2::aes(x = recount2_LV603, 
                                        y = LV87)) +
    ggplot2::geom_point(alpha = 0.7) +
    ggplot2::geom_smooth(method = "lm") +
    ggplot2::theme_bw() +
    ggplot2::labs(x = "recount2 LV603", y = "SLE WB LV87") +
    ggplot2::theme(legend.position = "none", 
                   text = ggplot2::element_text(size = 15)) +
    ggplot2::annotate(geom = "text", x = -0.375, y = 1,  
                      label = "r-squared = 0.87") 

plot.file <- 
  file.path(plot.dir, "Banchereau_neutrophil_count_LV87_v_recLV603_scatter.png")
ggplot2::ggsave(plot.file, plot = ggplot2::last_plot())
Saving 7 x 7 in image

Distribution of correlation values

There are 987 latent variables from the recount2 PLIER model. Could any one of them be well-correlated with the Banchereau, et al. neutrophil counts by chance?

recount.b.t <- as.data.frame(t(sle.recount.b))
recount.b.t <- tibble::rownames_to_column(recount.b.t, "Sample")
head(recount.b.t)
b.count.df <- dplyr::inner_join(x = neutro.df[, c("Sample", 
                                                  "Neutrophil.Count")],
                                y = recount.b.t, by = "Sample")
rsq.all.lv <- (cor(x = b.count.df$Neutrophil.Count, 
                   y = b.count.df[, 3:ncol(b.count.df)])) ^ 2
rsq.df <- as.data.frame(matrix(rsq.all.lv, ncol = 1))
colnames(rsq.df) <- "r.squared"
ggplot2::ggplot(rsq.df, ggplot2::aes(x = r.squared)) +
  ggplot2::geom_density() +
  ggplot2::geom_segment(mapping = ggplot2::aes(x = rsq.df[603, ], 
                                               xend = rsq.df[603, ],
                                               y = 14, yend = 0),
                        arrow = grid::arrow(),
                        colour = "blue") +
  ggplot2::theme_bw() +
  ggplot2::theme(text = ggplot2::element_text(size = 13)) +
  ggplot2::labs(x = "R-squared", subtitle = "Neutrophil Count ~ LV Value", 
                title = "Distribution of R-squared values") +
  ggplot2::annotate(geom = "text", x = 0.36, y = 15, colour = "blue", 
                    label = "LV603", size = 5) 

plot.file <- 
  file.path(plot.dir, "Banchereau_neutrophil_count_recount2_lv_rsq.png")
ggplot2::ggsave(plot.file, plot = ggplot2::last_plot())
Saving 7 x 7 in image
which.max(rsq.all.lv)
[1] 603

recount2 LV603 has the highest R-squared with the Banchereau, et al. neutrophil counts.

Plasma cell

# get LV951 into data.frame form
plasma.lv.df <- as.data.frame(cbind(colnames(sle.recount.b), 
                                    sle.recount.b[951, ]))
colnames(plasma.lv.df) <- c("Sample", "recount2_LV951")
# join with SLE WB results
plasma.df <- dplyr::inner_join(sle.plasma.df, plasma.lv.df, by = "Sample") %>%
  dplyr::mutate(recount2_LV951 = as.numeric(as.character(recount2_LV951)))
Column `Sample` joining character vector and factor, coercing into character vector
head(plasma.df)
plasma.file <- file.path(results.dir, "plasma_cell_LVs_both_models.tsv")
readr::write_tsv(plasma.df, plasma.file)
plasma.df %>%
  ggplot2::ggplot(ggplot2::aes(x = Disease.Activity, 
                               y = recount2_LV951)) +
  ggplot2::geom_boxplot(notch = TRUE) +
  ggplot2::theme_bw() +
  ggplot2::labs(x = "Disease Activity") +
  ggplot2::theme(text = ggplot2::element_text(size = 15))

# check for statistical significance 
# (pairwise t-test is consistent with original publication as far as I can tell)
pairwise.t.test(x = plasma.df$recount2_LV951, 
                g = plasma.df$Disease.Activity, 
                p.adjust.method = "bonferroni")

    Pairwise comparisons using t tests with pooled SD 

data:  plasma.df$recount2_LV951 and plasma.df$Disease.Activity 

    DA1     DA2    
DA2 0.047   -      
DA3 5.0e-13 2.3e-06

P value adjustment method: bonferroni 

This is highly similar to what we saw in 06-sle-wb_cell_type.

Now we’ll plot the recount2 (recount2_LV951) and SLE WB model (LV52) results side by side as we did above with the neutrophil scatterplots.

sle.plot <- plasma.df %>%
  ggplot2::ggplot(ggplot2::aes(x = Disease.Activity, 
                               y = LV52)) +
  ggplot2::geom_boxplot(notch = TRUE) +
  ggplot2::theme_bw() +
  ggplot2::labs(x = "Disease Activity",
                title = "SLE WB PLIER model") +
  ggplot2::theme(plot.title = ggplot2::element_text(hjust = 0.5, 
                                                    face = "bold")) +
  ggplot2::theme(text = ggplot2::element_text(size = 15))
recount.plot <- plasma.df %>%
  ggplot2::ggplot(ggplot2::aes(x = Disease.Activity, 
                               y = recount2_LV951)) +
  ggplot2::geom_boxplot(notch = TRUE) +
  ggplot2::theme_bw() +
  ggplot2::labs(x = "Disease Activity",
                title = "recount2 PLIER model") +
  ggplot2::theme(plot.title = ggplot2::element_text(hjust = 0.5, 
                                                    face = "bold")) +
  ggplot2::theme(text = ggplot2::element_text(size = 15))
pdf(file.path(plot.dir, "Plasma_cell_LV_model_comparison.pdf"), width = 14,
    height = 7)
gridExtra::grid.arrange(sle.plot, recount.plot, ncol = 2)
dev.off()
null device 
          1 
LS0tCnRpdGxlOiAiQmFuY2hlcmVhdSwgZXQgYWwuIGNlbGwgdHlwZSBhbmFseXNlcyB3aXRoIHJlY291bnQyIG1vZGVsIgpvdXRwdXQ6ICAgCiAgaHRtbF9ub3RlYm9vazogCiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQotLS0KCioqSi4gVGFyb25pIDIwMTgqKgoKSW4gYDA2LXNsZS13Yl9jZWxsX3R5cGVgLCB3ZSBleGFtaW5lZCBob3cgTFZzIGZyb20gdGhlIFNMRSBXQiBQTElFUiBtb2RlbCAKKGByZXN1bHRzLzA1L1NMRS1XQl9QTElFUl9tb2RlbC5SRFNgKSBjb21wYXJlZCB0byBrbm93biBwYXR0ZXJucyBpbiB0aGUKW0JhbmNoZXJlYXUsIGV0IGFsLl0oaHR0cHM6Ly9kb2kub3JnLzEwLjEwMTYvai5jZWxsLjIwMTYuMDMuMDA4KSBkYXRhIHNldCB0aGF0IAp3YXMgaW5jbHVkZWQgaW4gdGhlIFNMRSBXQiBjb21wZW5kaXVtIChhbmQgdGhlcmVmb3JlLCB0cmFpbmluZykuCgpTcGVjaWZpY2FsbHksIHdlIGFza2VkOgoKKiBBcmUgdGhlcmUgbmV1dHJvcGhpbC1hc3NvY2lhdGVkIExWcyB0aGF0IGFyZSBjb3JyZWxhdGVkIHdpdGggbmV1dHJvcGhpbCAKICBjb3VudHM/CiogQXJlIHRoZXJlIHBsYXNtYSBjZWxsLWFzc29jaWF0ZWQgTFZzIHRoYXQgZGVtb25zdHJhdGUgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24KICBiZXR3ZWVuIGRpc2Vhc2UgYWN0aXZpdHkgZ3JvdXBzIChzcGVjaWZpY2FsbHksIGhpZ2hlciB2YWx1ZXMgaW4gbW9yZSBzZXZlcmUgCiAgZGlzZWFzZSk/CiAgClRoZSBhbnN3ZXIgdG8gYm90aCBvZiB0aGVzZSBxdWVzdGlvbnMgaXMgeWVzLiAKVGhpcyBkZW1vbnN0cmF0ZWQgdGhlIHV0aWxpdHkgb2YgUExJRVIgZm9yIGV4dHJhY3RpbmcgY2VsbCB0eXBlIHBhdHRlcm5zIGZyb20gCmhldGVyb2dlbmVvdXMgKGF0IGxlYXN0IGluIHRlcm1zIG9mIHBhdGllbnQgcG9wdWxhdGlvbiBhbmQgcGxhdGZvcm0pIGdlbmUgCmV4cHJlc3Npb24gZGF0YSBmcm9tIGEgc2luZ2xlIHRpc3N1ZSAod2hvbGUgYmxvb2QpIHRoYXQgaGFkIGJlZW4gYXNzZW1ibGVkIGludG8gCmEgY29tcGVuZGl1bSAoW2BncmVlbmVsYWIvcmhldW0tcGxpZXItZGF0YS9zbGUtd2JgXShodHRwczovL2dpdGh1Yi5jb20vZ3JlZW5lbGFiL3JoZXVtLXBsaWVyLWRhdGEvdHJlZS80YmU1NDc1NTNmMjRmZWNhYzllMmY1YzJiNDY5YTE3ZjlkZjI1M2YwL3NsZS13YikpLgoKU0xFIGlzIG5vdCBhIHJhcmUgY29uZGl0aW9uIC0tIHRob3VnaCB0aGUgbW9sZWN1bGFyIGhldGVyb2dlbmVpdHkgYW5kIAptdWx0aS10aXNzdWUgbmF0dXJlIG9mIHRoZSBkaXNlYXNlIHdhcnJhbnQgc29waGlzdGljYXRlZCBhcHByb2FjaGVzIHRvIHRoZSAKYW5hbHlzaXMgb2YgdHJhbnNjcmlwdG9taWMgZGF0YS4KKFRoaXMgU0xFIFdCIGNvbXBlbmRpdW0gaXMgY29tcHJpc2VkIG9mIDE2NDAgc2FtcGxlcyBhbmQgaXMgX2luY29tcGxldGVfOyB3ZSAKcGlja2VkIDcgZGF0YSBzZXRzIGJhc2VkIG9uIHRoZSBwbGF0Zm9ybSB1c2VkIGFuZCB0aGUgYXZhaWxhYmlsaXR5IG9mIHJhdyBkYXRhLikgClRodXMsIHdlIGNhbiB1c2UgZGF0YS1pbnRlbnNpdmUgYXBwcm9hY2hlcyAoZS5nLiwgUExJRVIpIHRoYXQgYXJlIGluYXBwcm9wcmlhdGUgCmZvciBzbWFsbGVyIGRhdGEgc2V0cyBhbmQsIGJ5IGV4dGVuc2lvbiwgcmFyZSBvciB1bmRlcnN0dWRpZWQgZGlzZWFzZXMuCgpVc2luZyBhICJzaG92ZWwtcmVhZHkiIGRhdGEgc2V0IGxpa2UgW3JlY291bnQyXShodHRwczovL2podWJpb3N0YXRpc3RpY3Muc2hpbnlhcHBzLmlvL3JlY291bnQvKSAKdG8gdHJhaW4gYSBQTElFUiBtb2RlbCAoc2VlIFtgZ3JlZW5lbGFiL3JoZXVtLXBsaWVyLWRhdGEvcmVjb3VudDJgXShodHRwczovL2dpdGh1Yi5jb20vZ3JlZW5lbGFiL3JoZXVtLXBsaWVyLWRhdGEvdHJlZS80YmU1NDc1NTNmMjRmZWNhYzllMmY1YzJiNDY5YTE3ZjlkZjI1M2YwL3JlY291bnQyKSkKYW5kIGFwcGx5aW5nIHRoaXMgbW9kZWwgdG8gc21hbGxlciBkYXRhIHNldHMgbWF5ICJnZXQgYXJvdW5kIiB0aGUgZGF0YS1pbnRlbnNpdmUKcmVxdWlyZW1lbnQsIGJ1dCBtb3JlIGludmVzdGlnYXRpb24gaXMgd2FycmFudGVkLgoKSW4gdGhpcyBub3RlYm9vaywgd2UgZXhhbWluZSB3aGV0aGVyIHdlIGNhbiB1c2UgdGhlIHJlY291bnQyIFBMSUVSIG1vZGVsIAooYGRhdGEvcmVjb3VudDJfUExJRVJfZGF0YS9yZWNvdW50X1BMSUVSX21vZGVsLlJEU2ApIHRvIGFuYWx5emUgbmV1dHJvcGhpbCBhbmQgCnBsYXNtYSBjZWxsIHBhdHRlcm5zIGluIHRoZSBCYW5jaGVyZWF1LCBldCBhbC4gZGF0YS4KSWYgdGhlIHJlY291bnQyIG1vZGVsIHBlcmZvcm1hbmNlIGlzIHNpbWlsYXIgdG8gdGhhdCBvZiB0aGUgU0xFIG1vZGVsCihleGFtaW5lZCBpbiBgMDYtc2xlLXdiX2NlbGxfdHlwZWApLCB0aGF0IGlzIGV2aWRlbmNlIHN1cHBvcnRpbmcgdGhlIHZhbGlkaXR5IApvZiB0aGlzIHRyYW5zZmVyIGxlYXJuaW5nIGFwcHJvYWNoLgoKIyMgRnVuY3Rpb25zIGFuZCBkaXJlY3Rvcnkgc2V0IHVwCgpgYGB7cn0KYCU+JWAgPC0gZHBseXI6OmAlPiVgCmxpYnJhcnkoUExJRVIpCgojIGN1c3RvbSBmdW5jdGlvbnMKc291cmNlKGZpbGUucGF0aCgidXRpbCIsICJwbGllcl91dGlsLlIiKSkKYGBgCgpgYGB7cn0KIyBwbG90IGFuZCByZXN1bHQgZGlyZWN0b3J5IHNldHVwIGZvciB0aGlzIG5vdGVib29rCnBsb3QuZGlyIDwtIGZpbGUucGF0aCgicGxvdHMiLCAiMDciKQpkaXIuY3JlYXRlKHBsb3QuZGlyLCByZWN1cnNpdmUgPSBUUlVFLCBzaG93V2FybmluZ3MgPSBGQUxTRSkKcmVzdWx0cy5kaXIgPC0gZmlsZS5wYXRoKCJyZXN1bHRzIiwgIjA3IikKZGlyLmNyZWF0ZShyZXN1bHRzLmRpciwgcmVjdXJzaXZlID0gVFJVRSwgc2hvd1dhcm5pbmdzID0gRkFMU0UpCmBgYAoKIyMgTG9hZCBkYXRhCgojIyMgcmVjb3VudDIgbW9kZWwKCmBgYHtyfQpyZWNvdW50LnBsaWVyIDwtIHJlYWRSRFMoZmlsZS5wYXRoKCJkYXRhIiwgInJlY291bnQyX1BMSUVSX2RhdGEiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicmVjb3VudF9QTElFUl9tb2RlbC5SRFMiKSkKYGBgCgojIyMgU0xFIFdCIGV4cHJlc3Npb24gZGF0YQoKYGBge3J9CnN5bWJvbC5maWxlIDwtIAogIGZpbGUucGF0aCgiZGF0YSIsICJleHByZXNzaW9uX2RhdGEiLCAKICAgICAgICAgICAgIlNMRV9XQl9hbGxfbWljcm9hcnJheV9RTl96dG9fYmVmb3JlX3dpdGhfR2VuZVN5bWJvbC5wY2wiKQpzbGUuZXhwcnMuZGYgPC0gcmVhZHI6OnJlYWRfdHN2KHN5bWJvbC5maWxlLCBwcm9ncmVzcyA9IEZBTFNFKQojIG1hdHJpeCB3aXRoIGdlbmUgc3ltYm9sIGFzIHJvd25hbWVzCmV4cHJzLm1hdCA8LSBkcGx5cjo6c2VsZWN0KHNsZS5leHBycy5kZiwgLUVudHJleklEKQpyb3duYW1lcyhleHBycy5tYXQpIDwtIGV4cHJzLm1hdCRHZW5lU3ltYm9sCmV4cHJzLm1hdCA8LSBhcy5tYXRyaXgoZHBseXI6OnNlbGVjdChleHBycy5tYXQsIC1HZW5lU3ltYm9sKSkKYGBgCgojIyMgU0xFIFdCIGNlbGwgdHlwZSBMViBkZnMKCkZyb20gYDA2LXNsZS13Yl9jZWxsX3R5cGVgCgpgYGB7cn0KbmV1dHJvLmZpbGUgPC0gZmlsZS5wYXRoKCJyZXN1bHRzIiwgIjA2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICJCYW5jaGVyZWF1X2NvdW50X1NMRV9tb2RlbF9uZXV0cm9waGlsX0xWLnRzdiIpCnNsZS5uZXV0cm8uZGYgPC0gcmVhZHI6OnJlYWRfdHN2KG5ldXRyby5maWxlKQpwbGFzbWEuZmlsZSA8LSBmaWxlLnBhdGgoInJlc3VsdHMiLCAiMDYiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICJCYW5jaGVyZWF1X0RBX2dyb3VwX1NMRV9tb2RlbF9wbGFzbWFfY2VsbF9MVnMudHN2IikKc2xlLnBsYXNtYS5kZiA8LSByZWFkcjo6cmVhZF90c3YocGxhc21hLmZpbGUpCmBgYAoKIyMgQ2VsbCB0eXBlLWFzc29jaWF0ZWQgTFZzIGluIHRoZSByZWNvdW50MiBtb2RlbAoKYGBge3J9CiMgc3VtbWFyeSBpbmZvcm1hdGlvbiBmcm9tIHRoZSByZWNvdW50MiBQTElFUiBtb2RlbApyZWNvdW50LnN1bW1hcnkgPC0gcmVjb3VudC5wbGllciRzdW1tYXJ5CmBgYAoKIyMjIE5ldXRyb3BoaWwKCkFyZSB0aGVyZSByZWNvdW50MiBMVnMgYXNzb2NpYXRlZCB3aXRoIG5ldXRyb3BoaWwgZ2VuZSBzZXRzPwoKYGBge3J9CnJlY291bnQuc3VtbWFyeSAlPiUKICBkcGx5cjo6ZmlsdGVyKGdyZXBsKCJOZXV0cm9waGlsIiwgcGF0aHdheSksIAogICAgICAgICAgICAgICAgRkRSIDwgMC4wNSkKYGBgCgpUaGUgZm9sbG93aW5nIGxhdGVudCB2YXJpYWJsZXMgbG9vayBtb3N0IHByb21pc2luZzogYExWNTI0YCwgYExWNjAzYCwgYExWOTg1YAooYEFVQyA+IDAuNzVgIGZvciBib3RoIGBTVk0gTmV1dHJvcGhpbHNgIGFuZCBgSVJJU19OZXV0cm9waGlsLVJlc3RpbmdgKQoKV2hhdCBvdGhlciBnZW5lIHNldHMgYXJlIGFzc29jaWF0ZWQgd2l0aCB0aGVzZSBMVnM/CgpgYGB7cn0KcmVjb3VudC5zdW1tYXJ5ICU+JQogIGRwbHlyOjpmaWx0ZXIoYExWIGluZGV4YCAlaW4lIGMoNTI0LCA2MDMsIDk4NSksCiAgICAgICAgICAgICAgICBGRFIgPCAwLjA1KSAlPiUKICBkcGx5cjo6YXJyYW5nZShgTFYgaW5kZXhgLCBkZXNjKEFVQykpCmBgYAoKU29tZSBub3RlcyBvbiB0aGVzZSByZXN1bHRzOgoKKiBgTFY1MjRgIGlzIGFsc28gYXNzb2NpYXRlZCB3aXRoIHRoZSBmb2xsb3dpbmcgbW9ub2N5dGUtcmVsYXRlZCBnZW5lIHNldHM6IApgU1ZNIE1vbm9jeXRlc2AsIGBETUFQX01PTk8xYCwgYW5kIGBETUFQX01PTk8yYC4gQWdhaW4sIHdlIHdvdWxkIGxpa2Ugc29tZXRoaW5nCnRoYXQgdGVsbHMgdXMgYWJvdXQgbmV1dHJvcGhpbHMgX29ubHlfIGlmIGF0IGFsbCBwb3NzaWJsZS4KKiBgTFY2MDNgIGlzIGFzc29jaWF0ZWQgd2l0aCB0d28gb3RoZXIgZ2VuZSBzZXRzLCBbYFBJRF9JTDhDWENSMl9QQVRIV0FZYF0oaHR0cDovL3NvZnR3YXJlLmJyb2FkaW5zdGl0dXRlLm9yZy9nc2VhL21zaWdkYi9jYXJkcy9QSURfSUw4X0NYQ1IyX1BBVEhXQVkuaHRtbCkgYW5kCltgU0lHX1BJUDNfU0lHTkFMSU5HX0lOX0JfTFlNUEhPQ1lURVNgXShodHRwOi8vc29mdHdhcmUuYnJvYWRpbnN0aXR1dGUub3JnL2dzZWEvbXNpZ2RiL2NhcmRzL1NJR19QSVAzX1NJR05BTElOR19JTl9CX0xZTVBIT0NZVEVTLmh0bWwpLgpJdCdzIHdvcnRoIG5vdGluZyB0aGF0IGludGVybGV1a2luIDggKGBJTDhgKSBpcyBhIG5ldXRyb3BoaWwgY2hlbW9hdHRyYWN0YW50LCAKYW5kIGBTSUdfUElQM19TSUdOQUxJTkdfSU5fQl9MWU1QSE9DWVRFU2AgW21heSBqdXN0IGJlIHJlbGF0ZWRdKGh0dHA6Ly9zb2Z0d2FyZS5icm9hZGluc3RpdHV0ZS5vcmcvZ3NlYS9tc2lnZGIvY29tcHV0ZV9vdmVybGFwcy5qc3A/Z2VuZVNldE5hbWU9U0lHX1BJUDNfU0lHTkFMSU5HX0lOX0JfTFlNUEhPQ1lURVMmY29sbGVjdGlvbj1DMikgdG8gUEkzSyBzaWduYWxpbmcgJiBjaGVtb3RheGlzIGluIGdlbmVyYWwuCiogYExWOTg1YCBpcyBhbHNvIGFzc29jaWF0ZWQgd2l0aCBgU0lHX1BJUDNfU0lHTkFMSU5HX0lOX0JfTFlNUEhPQ1lURVNgIGFuZApbYFBJRF9GQ0VSMVBBVEhXQVlgXShodHRwOi8vc29mdHdhcmUuYnJvYWRpbnN0aXR1dGUub3JnL2dzZWEvbXNpZ2RiL2NhcmRzL1BJRF9GQ0VSMV9QQVRIV0FZLmh0bWwpLgpgRkNFUjFgIHJlZmVycyB0byBGYy1lcHNpbG9uIHJlY2VwdG9yIDEgd2hpY2ggaXMgbWFpbmx5IGV4cHJlc3NlZCBvbiBvdGhlcgpncmFudWxvY3l0ZXMuCgoKIyMjIyBVIG1hdHJpeAoKYGBge3J9ClBMSUVSOjpwbG90VShwbGllclJlcyA9IHJlY291bnQucGxpZXIsCiAgICAgICAgICAgICBwdmFsLmN1dG9mZiA9IDFlLTA2LAogICAgICAgICAgICAgaW5kZXhDb2wgPSBjKDUyNCwgNjAzLCA5ODUpLAogICAgICAgICAgICAgdG9wID0gMTApCmBgYAoKYGBge3J9CnBuZyhmaWxlLnBhdGgocGxvdC5kaXIsICJyZWNvdW50Ml9uZXV0cm9waGlsX1VwbG90LnBuZyIpLCAKICAgIHJlcyA9IDMwMCwgd2lkdGggPSA3LCBoZWlnaHQgPSA3LCB1bml0cyA9ICJpbiIpClBMSUVSOjpwbG90VShwbGllclJlcyA9IHJlY291bnQucGxpZXIsCiAgICAgICAgICAgICBwdmFsLmN1dG9mZiA9IDFlLTA2LAogICAgICAgICAgICAgaW5kZXhDb2wgPSBjKDUyNCwgNjAzLCA5ODUpLAogICAgICAgICAgICAgdG9wID0gMTApCmRldi5vZmYoKQpgYGAKCiMjIyBQbGFzbWEgY2VsbAoKQXJlIHRoZXJlIExWcyBhc3NvY2lhdGVkIHdpdGggcGxhc21hIGNlbGwgZ2VuZSBzZXRzIGluIHRoZSByZWNvdW50MiBtb2RlbD8KCmBgYHtyfQpyZWNvdW50LnN1bW1hcnkgJT4lCiAgZHBseXI6OmZpbHRlcihncmVwbCgiUGxhc21hIiwgcGF0aHdheSksIAogICAgICAgICAgICAgICAgRkRSIDwgMC4wNSkKYGBgCgpXaGF0IG90aGVyIGdlbmUgc2V0cyBhcmUgYXNzb2NpYXRlZCB3aXRoIGBMVjk1MWA/CgpgYGB7cn0KcmVjb3VudC5zdW1tYXJ5ICU+JQogIGRwbHlyOjpmaWx0ZXIoYExWIGluZGV4YCA9PSA5NTEpCmBgYAoKYEtFR0dfSU5URVNUSU5BTF9JTU1VTkVfTkVUV09SS19GT1JfSUdBX1BST0RVQ1RJT05gIGlzIG5vdCBhIHNpZ25pZmljYW50IAphc3NvY2lhdGlvbiAoYEZEUiA9IDAuMTNgKQpUaGVyZSB3ZXJlIDIgTFZzIGZyb20gdGhlIFNMRSBXQiBQTElFUiBtb2RlbCB0aGF0IHdlcmUgYXNzb2NpYXRlZCB3aXRoIHBsYXNtYQpjZWxsIGdlbmUgc2V0cy4gClRob3NlIExWcyB3ZXJlIGFsc28gYXNzb2NpYXRlZCB3aXRoIG90aGVyIHByb2Nlc3NlcyAoZS5nLiwgCmBSRUFDVE9NRV9VTkZPTERFRF9QUk9URUlOX1JFU1BPTlNFYCwgYEtFR0dfSEVER0VIT0dfU0lHTkFMSU5HX1BBVEhXQVlgKQoKIyMgVHJhbnNmb3JtIFNMRSBXQiBkYXRhCgpgYGB7cn0Kc2xlLnJlY291bnQuYiA8LSBHZXROZXdEYXRhQihleHBycy5tYXQgPSBleHBycy5tYXQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGxpZXIubW9kZWwgPSByZWNvdW50LnBsaWVyKQpiLmZpbGUgPC0gZmlsZS5wYXRoKHJlc3VsdHMuZGlyLCAiU0xFLVdCX0JfbWF0cml4X3JlY291bnQyX21vZGVsLlJEUyIpCnNhdmVSRFMoc2xlLnJlY291bnQuYiwgZmlsZSA9IGIuZmlsZSkKYGBgCgojIyBCYW5jaGVyZWF1LCBldCBhbC4gY2VsbCB0eXBlIHJlc3VsdHMKCiMjIyBOZXV0cm9waGlsCgpgYGB7cn0KbmV1dHJvLmx2LmRmIDwtIGFzLmRhdGEuZnJhbWUoY2JpbmQoY29sbmFtZXMoc2xlLnJlY291bnQuYiksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0KHNsZS5yZWNvdW50LmJbYyg1MjQsIDYwMywgOTg1KSwgXSkpKQpjb2xuYW1lcyhuZXV0cm8ubHYuZGYpIDwtIGMoIlNhbXBsZSIsICJyZWNvdW50Ml9MVjUyNCIsICJyZWNvdW50Ml9MVjYwMyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgInJlY291bnQyX0xWOTg1IikKYGBgCgpgYGB7cn0KIyBqb2luIHdpdGggc2xlIHdiIHJlc3VsdHMgJiBuZXV0cm9waGlsIGNvdW50cwpuZXV0cm8uZGYgPC0gZHBseXI6OmlubmVyX2pvaW4oc2xlLm5ldXRyby5kZiwgbmV1dHJvLmx2LmRmKSAlPiUKICAgICAgICAgICAgICBkcGx5cjo6bXV0YXRlKHJlY291bnQyX0xWNTI0ID0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIocmVjb3VudDJfTFY1MjQpKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWNvdW50Ml9MVjYwMyA9IGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKHJlY291bnQyX0xWNjAzKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWNvdW50Ml9MVjk4NSA9IGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKHJlY291bnQyX0xWOTg1KSkpCmhlYWQobmV1dHJvLmRmKQpgYGAKCmBgYHtyfQpuZXV0cm8ub3V0LmZpbGUgPC0gZmlsZS5wYXRoKHJlc3VsdHMuZGlyLCAibmV1dHJvcGhpbF9jb3VudF9MVl9ib3RoX21vZGVscy50c3YiKQpyZWFkcjo6d3JpdGVfdHN2KG5ldXRyby5kZiwgcGF0aCA9IG5ldXRyby5vdXQuZmlsZSkKYGBgCgpgYGB7cn0KIyBmdW5jdGlvbiBmb3Igc2NhdHRlciBwbG90cywgZ2l2ZW4gbHYgKGEgc3RyaW5nIGluZGljYXRpbmcgdGhlIExWIG9mIGludGVyZXN0KQojIG1ha2UgYSBzY2F0dGVyIHBsb3Qgd2hlcmUgdGhlIExWIGlzIHRoZSB4IHZhcmlhYmxlLCBuZXV0cm9waGlsIGNvdW50IGlzIHRoZQojIHkgdmFyaWFibGUgJiBmaXQgYSBsaW5lIHdpdGggZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIikKIyBhbHNvIHdpbGwgYW5ub3RhdGUgdGhlIHBsb3Qgd2l0aCB0aGUgc3VwcGxpZWQgci1zcXVhcmVkIHZhbHVlIChyc3EgYXJnKQojIHdoZXJlIHRoZSB0ZXh0IGlzIHBsYWNlZCBpcyBhdXRvbWF0aWNhbGx5IGNob3NlbiBmcm9tIHRoZSB4IGFuZCB5IHZhbHVlcwojIG5lZWRzIHRvIGJlIHVzZWQgaW4gdGhpcyBnbG9iYWwgZW52aXJvbm1lbnQKTFZTY2F0dGVyIDwtIGZ1bmN0aW9uKGx2LCByc3EpIHsKICB5LnZhciA8LSAiTmV1dHJvcGhpbC5Db3VudCIKICAKICAjIGNhbGN1bGF0ZSB3aGVyZSB0byBwdXQgdGhlIHItc3F1YXJlZCB2YWx1ZQogIHgucmFuZ2UgPC0gbWF4KG5ldXRyby5kZlssIGx2XSkgLSBtaW4obmV1dHJvLmRmWywgbHZdKQogIHguY29vcmQgPC0gbWluKG5ldXRyby5kZlssIGx2XSkgKyAoeC5yYW5nZSAqIDAuMTUpCiAgeS5yYW5nZSA8LSBtYXgobmV1dHJvLmRmWywgeS52YXJdKSAtIG1pbihuZXV0cm8uZGZbLCB5LnZhcl0pCiAgeS5jb29yZCA8LSBtYXgobmV1dHJvLmRmWywgeS52YXJdKSAtICh5LnJhbmdlICogMC4xNSkKICAKICBnZ3Bsb3QyOjpnZ3Bsb3QobmV1dHJvLmRmLCBnZ3Bsb3QyOjphZXNfc3RyaW5nKHggPSBsdiwgeSA9IHkudmFyKSkgKwogICAgZ2dwbG90Mjo6Z2VvbV9wb2ludChhbHBoYSA9IDAuNykgKwogICAgZ2dwbG90Mjo6Z2VvbV9zbW9vdGgobWV0aG9kID0gImxtIikgKwogICAgZ2dwbG90Mjo6dGhlbWVfYncoKSArCiAgICBnZ3Bsb3QyOjpsYWJzKHkgPSAiTmV1dHJvcGhpbCBDb3VudCIpICsKICAgIGdncGxvdDI6OnRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwgCiAgICAgICAgICAgICAgICAgICB0ZXh0ID0gZ2dwbG90Mjo6ZWxlbWVudF90ZXh0KHNpemUgPSAxNSkpICsKICAgIGdncGxvdDI6OmFubm90YXRlKCJ0ZXh0IiwgeCA9IHguY29vcmQsIHkgPSB5LmNvb3JkLCAKICAgICAgICAgICAgICAgICAgICAgIGxhYmVsID0gcGFzdGUoInItc3F1YXJlZCA9IiwgcnNxKSkKfQpgYGAKCiMjIyMgTFY1MjQKCmBgYHtyfQpzdW1tYXJ5KGxtKG5ldXRyby5kZiROZXV0cm9waGlsLkNvdW50IH4gbmV1dHJvLmRmJHJlY291bnQyX0xWNTI0KSkKYGBgCgpgYGB7cn0KTFZTY2F0dGVyKGx2ID0gInJlY291bnQyX0xWNTI0IiwgcnNxID0gMC4zNCkKYGBgCgoKIyMjIyBMVjYwMwoKYGBge3J9CnN1bW1hcnkobG0obmV1dHJvLmRmJE5ldXRyb3BoaWwuQ291bnQgfiBuZXV0cm8uZGYkcmVjb3VudDJfTFY2MDMpKQpgYGAKCmBgYHtyfQpMVlNjYXR0ZXIobHYgPSAicmVjb3VudDJfTFY2MDMiLCByc3EgPSAwLjM2KQpgYGAKCiMjIyMgTFY5ODUKCgpgYGB7cn0Kc3VtbWFyeShsbShuZXV0cm8uZGYkTmV1dHJvcGhpbC5Db3VudCB+IG5ldXRyby5kZiRyZWNvdW50Ml9MVjk4NSkpCmBgYAoKYGBge3J9CkxWU2NhdHRlcihsdiA9ICJyZWNvdW50Ml9MVjk4NSIsIHJzcSA9IDAuMzIpCmBgYAoKVGhlIG5ldXRyb3BoaWwtYXNzb2NpYXRlZCBMVnMgZnJvbSB0aGUgcmVjb3VudDIgbW9kZWwgZ2VuZXJhbGx5IHBlcmZvcm0gYXMgd2VsbAphcyB0aGUgbmV1dHJvcGhpbC1hc3NvY2lhdGVkIExWcyBmcm9tIHRoZSBTTEUgV0IgUExJRVIgbW9kZWwuCgpXZSdsbCBwbG90IHRoZSByZXN1bHRzIGZyb20gYFNMRSBXQiBMVjg3YCBhbmQgYHJlY291bnQyIExWNjAzYCB0b2dldGhlciBiZWNhdXNlCnRoZXNlIGFyZSBib3RoIExWcyB0aGF0IGFyZSBub3Qgc2lnbmlmaWNhbnRseSBhc3NvY2lhdGVkIHdpdGggbW9ub2N5dGUgCnNpZ25hdHVyZXMgYW5kIGhhdmUgaGlnaCBBVUMgZm9yIHRoZSBuZXV0cm9waGlsIGdlbmUgc2V0cy4KCgpgYGB7cn0Kc2xlLnBsb3QgPC0gTFZTY2F0dGVyKCJMVjg3IiwgcnNxID0gMC4yOSkgKwogIGdncGxvdDI6OmxhYnModGl0bGUgPSAiU0xFIFdCIFBMSUVSIG1vZGVsIikgKwogIGdncGxvdDI6OnRoZW1lKHBsb3QudGl0bGUgPSBnZ3Bsb3QyOjplbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFjZSA9ICJib2xkIikpCmBgYAoKYGBge3J9CnJlY291bnQucGxvdCA8LSBMVlNjYXR0ZXIoInJlY291bnQyX0xWNjAzIiwgcnNxID0gMC4zNikgKwogIGdncGxvdDI6OmxhYnModGl0bGUgPSAicmVjb3VudDIgUExJRVIgbW9kZWwiKSArCiAgZ2dwbG90Mjo6dGhlbWUocGxvdC50aXRsZSA9IGdncGxvdDI6OmVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYWNlID0gImJvbGQiKSkKYGBgCgpgYGB7cn0KcGRmKGZpbGUucGF0aChwbG90LmRpciwgIk5ldXRyb3BoaWxfTFZfbW9kZWxfY29tcGFyaXNvbi5wZGYiKSwgd2lkdGggPSAxNCwKICAgIGhlaWdodCA9IDcpCmdyaWRFeHRyYTo6Z3JpZC5hcnJhbmdlKHNsZS5wbG90LCByZWNvdW50LnBsb3QsIG5jb2wgPSAyKQpkZXYub2ZmKCkKYGBgCgojIyMjIENvcnJlbGF0aW9uIGJldHdlZW4gTFYgdmFsdWVzIGZyb20gZGlmZmVyZW50IG1vZGVscwoKYGBge3J9CnN1bW1hcnkobG0obmV1dHJvLmRmJExWODcgfiBuZXV0cm8uZGYkcmVjb3VudDJfTFY2MDMpKQpgYGAKCmBgYHtyfQpnZ3Bsb3QyOjpnZ3Bsb3QobmV1dHJvLmRmLCBnZ3Bsb3QyOjphZXMoeCA9IHJlY291bnQyX0xWNjAzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBMVjg3KSkgKwogICAgZ2dwbG90Mjo6Z2VvbV9wb2ludChhbHBoYSA9IDAuNykgKwogICAgZ2dwbG90Mjo6Z2VvbV9zbW9vdGgobWV0aG9kID0gImxtIikgKwogICAgZ2dwbG90Mjo6dGhlbWVfYncoKSArCiAgICBnZ3Bsb3QyOjpsYWJzKHggPSAicmVjb3VudDIgTFY2MDMiLCB5ID0gIlNMRSBXQiBMVjg3IikgKwogICAgZ2dwbG90Mjo6dGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLCAKICAgICAgICAgICAgICAgICAgIHRleHQgPSBnZ3Bsb3QyOjplbGVtZW50X3RleHQoc2l6ZSA9IDE1KSkgKwogICAgZ2dwbG90Mjo6YW5ub3RhdGUoZ2VvbSA9ICJ0ZXh0IiwgeCA9IC0wLjM3NSwgeSA9IDEsICAKICAgICAgICAgICAgICAgICAgICAgIGxhYmVsID0gInItc3F1YXJlZCA9IDAuODciKSAKYGBgCgpgYGB7cn0KcGxvdC5maWxlIDwtIAogIGZpbGUucGF0aChwbG90LmRpciwgIkJhbmNoZXJlYXVfbmV1dHJvcGhpbF9jb3VudF9MVjg3X3ZfcmVjTFY2MDNfc2NhdHRlci5wbmciKQpnZ3Bsb3QyOjpnZ3NhdmUocGxvdC5maWxlLCBwbG90ID0gZ2dwbG90Mjo6bGFzdF9wbG90KCkpCmBgYAoKCiMjIyMgRGlzdHJpYnV0aW9uIG9mIGNvcnJlbGF0aW9uIHZhbHVlcwoKVGhlcmUgYXJlIDk4NyBsYXRlbnQgdmFyaWFibGVzIGZyb20gdGhlIHJlY291bnQyIFBMSUVSIG1vZGVsLiAKQ291bGQgYW55IG9uZSBvZiB0aGVtIGJlIHdlbGwtY29ycmVsYXRlZCB3aXRoIHRoZSBCYW5jaGVyZWF1LCBldCBhbC4gCm5ldXRyb3BoaWwgY291bnRzIGJ5IGNoYW5jZT8KCmBgYHtyfQpyZWNvdW50LmIudCA8LSBhcy5kYXRhLmZyYW1lKHQoc2xlLnJlY291bnQuYikpCnJlY291bnQuYi50IDwtIHRpYmJsZTo6cm93bmFtZXNfdG9fY29sdW1uKHJlY291bnQuYi50LCAiU2FtcGxlIikKaGVhZChyZWNvdW50LmIudCkKYGBgCgpgYGB7cn0KYi5jb3VudC5kZiA8LSBkcGx5cjo6aW5uZXJfam9pbih4ID0gbmV1dHJvLmRmWywgYygiU2FtcGxlIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk5ldXRyb3BoaWwuQ291bnQiKV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IHJlY291bnQuYi50LCBieSA9ICJTYW1wbGUiKQpgYGAKCmBgYHtyfQpyc3EuYWxsLmx2IDwtIChjb3IoeCA9IGIuY291bnQuZGYkTmV1dHJvcGhpbC5Db3VudCwgCiAgICAgICAgICAgICAgICAgICB5ID0gYi5jb3VudC5kZlssIDM6bmNvbChiLmNvdW50LmRmKV0pKSBeIDIKcnNxLmRmIDwtIGFzLmRhdGEuZnJhbWUobWF0cml4KHJzcS5hbGwubHYsIG5jb2wgPSAxKSkKY29sbmFtZXMocnNxLmRmKSA8LSAici5zcXVhcmVkIgpgYGAKCmBgYHtyfQpnZ3Bsb3QyOjpnZ3Bsb3QocnNxLmRmLCBnZ3Bsb3QyOjphZXMoeCA9IHIuc3F1YXJlZCkpICsKICBnZ3Bsb3QyOjpnZW9tX2RlbnNpdHkoKSArCiAgZ2dwbG90Mjo6Z2VvbV9zZWdtZW50KG1hcHBpbmcgPSBnZ3Bsb3QyOjphZXMoeCA9IHJzcS5kZls2MDMsIF0sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHhlbmQgPSByc3EuZGZbNjAzLCBdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSAxNCwgeWVuZCA9IDApLAogICAgICAgICAgICAgICAgICAgICAgICBhcnJvdyA9IGdyaWQ6OmFycm93KCksCiAgICAgICAgICAgICAgICAgICAgICAgIGNvbG91ciA9ICJibHVlIikgKwogIGdncGxvdDI6OnRoZW1lX2J3KCkgKwogIGdncGxvdDI6OnRoZW1lKHRleHQgPSBnZ3Bsb3QyOjplbGVtZW50X3RleHQoc2l6ZSA9IDEzKSkgKwogIGdncGxvdDI6OmxhYnMoeCA9ICJSLXNxdWFyZWQiLCBzdWJ0aXRsZSA9ICJOZXV0cm9waGlsIENvdW50IH4gTFYgVmFsdWUiLCAKICAgICAgICAgICAgICAgIHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiBSLXNxdWFyZWQgdmFsdWVzIikgKwogIGdncGxvdDI6OmFubm90YXRlKGdlb20gPSAidGV4dCIsIHggPSAwLjM2LCB5ID0gMTUsIGNvbG91ciA9ICJibHVlIiwgCiAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSAiTFY2MDMiLCBzaXplID0gNSkgCmBgYAoKYGBge3J9CnBsb3QuZmlsZSA8LSAKICBmaWxlLnBhdGgocGxvdC5kaXIsICJCYW5jaGVyZWF1X25ldXRyb3BoaWxfY291bnRfcmVjb3VudDJfbHZfcnNxLnBuZyIpCmdncGxvdDI6Omdnc2F2ZShwbG90LmZpbGUsIHBsb3QgPSBnZ3Bsb3QyOjpsYXN0X3Bsb3QoKSkKYGBgCgpgYGB7cn0Kd2hpY2gubWF4KHJzcS5hbGwubHYpCmBgYAoKYHJlY291bnQyIExWNjAzYCBoYXMgdGhlIGhpZ2hlc3QgUi1zcXVhcmVkIHdpdGggdGhlIEJhbmNoZXJlYXUsIGV0IGFsLiAKbmV1dHJvcGhpbCBjb3VudHMuCgojIyMgUGxhc21hIGNlbGwKCmBgYHtyfQojIGdldCBMVjk1MSBpbnRvIGRhdGEuZnJhbWUgZm9ybQpwbGFzbWEubHYuZGYgPC0gYXMuZGF0YS5mcmFtZShjYmluZChjb2xuYW1lcyhzbGUucmVjb3VudC5iKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNsZS5yZWNvdW50LmJbOTUxLCBdKSkKY29sbmFtZXMocGxhc21hLmx2LmRmKSA8LSBjKCJTYW1wbGUiLCAicmVjb3VudDJfTFY5NTEiKQpgYGAKCmBgYHtyfQojIGpvaW4gd2l0aCBTTEUgV0IgcmVzdWx0cwpwbGFzbWEuZGYgPC0gZHBseXI6OmlubmVyX2pvaW4oc2xlLnBsYXNtYS5kZiwgcGxhc21hLmx2LmRmLCBieSA9ICJTYW1wbGUiKSAlPiUKICBkcGx5cjo6bXV0YXRlKHJlY291bnQyX0xWOTUxID0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIocmVjb3VudDJfTFY5NTEpKSkKaGVhZChwbGFzbWEuZGYpCmBgYAoKYGBge3J9CnBsYXNtYS5kZiAlPiUKICBnZ3Bsb3QyOjpnZ3Bsb3QoZ2dwbG90Mjo6YWVzKHggPSBEaXNlYXNlLkFjdGl2aXR5LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSByZWNvdW50Ml9MVjk1MSkpICsKICBnZ3Bsb3QyOjpnZW9tX2JveHBsb3Qobm90Y2ggPSBUUlVFKSArCiAgZ2dwbG90Mjo6dGhlbWVfYncoKSArCiAgZ2dwbG90Mjo6bGFicyh4ID0gIkRpc2Vhc2UgQWN0aXZpdHkiKSArCiAgZ2dwbG90Mjo6dGhlbWUodGV4dCA9IGdncGxvdDI6OmVsZW1lbnRfdGV4dChzaXplID0gMTUpKQpgYGAKCmBgYHtyfQojIGNoZWNrIGZvciBzdGF0aXN0aWNhbCBzaWduaWZpY2FuY2UgCiMgKHBhaXJ3aXNlIHQtdGVzdCBpcyBjb25zaXN0ZW50IHdpdGggb3JpZ2luYWwgcHVibGljYXRpb24gYXMgZmFyIGFzIEkgY2FuIHRlbGwpCnBhaXJ3aXNlLnQudGVzdCh4ID0gcGxhc21hLmRmJHJlY291bnQyX0xWOTUxLCAKICAgICAgICAgICAgICAgIGcgPSBwbGFzbWEuZGYkRGlzZWFzZS5BY3Rpdml0eSwgCiAgICAgICAgICAgICAgICBwLmFkanVzdC5tZXRob2QgPSAiYm9uZmVycm9uaSIpCmBgYAoKVGhpcyBpcyBoaWdobHkgc2ltaWxhciB0byB3aGF0IHdlIHNhdyBpbiBgMDYtc2xlLXdiX2NlbGxfdHlwZWAuCgpOb3cgd2UnbGwgcGxvdCB0aGUgcmVjb3VudDIgKGByZWNvdW50Ml9MVjk1MWApIGFuZCBTTEUgV0IgbW9kZWwgKGBMVjUyYCkgCnJlc3VsdHMgc2lkZSBieSBzaWRlIGFzIHdlIGRpZCBhYm92ZSB3aXRoIHRoZSBuZXV0cm9waGlsIHNjYXR0ZXJwbG90cy4KCmBgYHtyfQpzbGUucGxvdCA8LSBwbGFzbWEuZGYgJT4lCiAgZ2dwbG90Mjo6Z2dwbG90KGdncGxvdDI6OmFlcyh4ID0gRGlzZWFzZS5BY3Rpdml0eSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gTFY1MikpICsKICBnZ3Bsb3QyOjpnZW9tX2JveHBsb3Qobm90Y2ggPSBUUlVFKSArCiAgZ2dwbG90Mjo6dGhlbWVfYncoKSArCiAgZ2dwbG90Mjo6bGFicyh4ID0gIkRpc2Vhc2UgQWN0aXZpdHkiLAogICAgICAgICAgICAgICAgdGl0bGUgPSAiU0xFIFdCIFBMSUVSIG1vZGVsIikgKwogIGdncGxvdDI6OnRoZW1lKHBsb3QudGl0bGUgPSBnZ3Bsb3QyOjplbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFjZSA9ICJib2xkIikpICsKICBnZ3Bsb3QyOjp0aGVtZSh0ZXh0ID0gZ2dwbG90Mjo6ZWxlbWVudF90ZXh0KHNpemUgPSAxNSkpCmBgYAoKYGBge3J9CnJlY291bnQucGxvdCA8LSBwbGFzbWEuZGYgJT4lCiAgZ2dwbG90Mjo6Z2dwbG90KGdncGxvdDI6OmFlcyh4ID0gRGlzZWFzZS5BY3Rpdml0eSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gcmVjb3VudDJfTFY5NTEpKSArCiAgZ2dwbG90Mjo6Z2VvbV9ib3hwbG90KG5vdGNoID0gVFJVRSkgKwogIGdncGxvdDI6OnRoZW1lX2J3KCkgKwogIGdncGxvdDI6OmxhYnMoeCA9ICJEaXNlYXNlIEFjdGl2aXR5IiwKICAgICAgICAgICAgICAgIHRpdGxlID0gInJlY291bnQyIFBMSUVSIG1vZGVsIikgKwogIGdncGxvdDI6OnRoZW1lKHBsb3QudGl0bGUgPSBnZ3Bsb3QyOjplbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFjZSA9ICJib2xkIikpICsKICBnZ3Bsb3QyOjp0aGVtZSh0ZXh0ID0gZ2dwbG90Mjo6ZWxlbWVudF90ZXh0KHNpemUgPSAxNSkpCmBgYAoKYGBge3J9CnBkZihmaWxlLnBhdGgocGxvdC5kaXIsICJQbGFzbWFfY2VsbF9MVl9tb2RlbF9jb21wYXJpc29uLnBkZiIpLCB3aWR0aCA9IDE0LAogICAgaGVpZ2h0ID0gNykKZ3JpZEV4dHJhOjpncmlkLmFycmFuZ2Uoc2xlLnBsb3QsIHJlY291bnQucGxvdCwgbmNvbCA9IDIpCmRldi5vZmYoKQpgYGAKCg==