Our SLE WB compendium includes a trial of interferon-alpha-kinoid (IFN-K), which should block type I IFN only and therefore the expression levels of IFN-alpha/IFN-beta gene signatures should decrease during treatment (leaving IFN-gamma signature expression relatively unchanged). A trial of AMG-811, which is a monoclonal antibody against IFN-gamma, is also included. We expected to see IFN-gamma gene expression decrease in the AMG-811 during treatment.

In this notebook, we’ll tidy data in preparation for analyzing changes in IFN-related gene expression using a couple different gene sets or models – modular transcriptional analyses (from Chiche, et al. 2014.), a PLIER model trained on the SLE WB compendium and a PLIER model trained on recount2 data.

For more information about the types of IFNs, see the intro to 08-identify_ifn_LVs.

Functions and directory set up

`%>%` <- dplyr::`%>%`

Functions specifically for modular analyses

ReadInTribeConversions <- function(filename) {
  # Reads in csv file downloaded from Tribe and returns character vector. 
  # The gene identifiers start at the 7th line. 
  #
  # Args:
  #   filename: full path to csv file containing the list of gene identifers
  #
  # Returns:
  #   geneset: a vector (character) of gene identifiers
  #
  
  geneset <- readLines(filename)
  geneset <- geneset[7:length(geneset)]
  geneset <- gsub("\t", "", geneset)
  return(geneset)
}
GetGeneSetMean <- function(gene.set, exprs) {
  # Summarize the expression levels of genes in a gene set by taking the mean
  # expression value of all genes in that gene set
  #
  # Args:
  #   gene.set: vector of gene identifiers -- corresponds to the geneset to be
  #             summarized
  #   exprs: data.frame that contains the expression values (rows are genes,
  #          columns are samples); first column -- "Gene" -- contains gene ids
  #          (match the type of identifiers used in gene.set)
  #
  # Returns:
  #   summary.df: a data.frame, 1 row corresponding to the mean expression 
  #               values for genes in gene.set, columns are samples
  #
  
  if (colnames(exprs)[1] != "Gene") {
    stop("The first column must contain gene identifiers and have
         the colname 'Gene'")
  }
  
  `%>%` <- dplyr::`%>%`
  summary.df <- exprs %>%
                  dplyr::filter(Gene %in% gene.set) %>% 
                  dplyr::select(-Gene) %>%
                  dplyr::summarise_all(mean)
  
  return(summary.df)
  
}
# plot and result directory setup for this notebook
plot.dir <- file.path("plots", "09")
dir.create(plot.dir, recursive = TRUE, showWarnings = FALSE)
results.dir <- file.path("results", "09")
dir.create(results.dir, recursive = TRUE, showWarnings = FALSE)
# set seed for reproducibility (plot jitter)
set.seed(123)

IFN-K

E-GEOD-39088; Lauwerys, et al. 2013.

First, I’ll summarize some relevant points from Lauwerys, et al.:

  • IFN-K is a therapeutic vaccine. It induces IFN-alpha antibodies in those who receive it. In turn, those antibodies will bind IFN-alpha and therefore reduce its activity/ability to stimulate the immune system.
  • Patients with SLE were treated with one of four doses of IFN-K or placebo.
  • Whole blood was collected from patients at days 0 (baseline), 112, and 168.
  • Healthy control blood was also included (2 samples from each control); one of these healthy control blood samples was treated with type I IFNs (IFN-alpha subtype(s) specifically) to derive an IFN-inducible gene expression signature.
  • The authors stratified patients into IFN-positive (n = 18) and IFN-negative (n = 9) groups based on the expression levels of the IFN-inducible gene signature at baseline.
  • Patients with a IFN-positive baseline signature had a reduction in IFN-inducible treatment during treatment.

We do not have information about which patients are in which of these two groups, so we’ll have to designate IFN-positive and IFN-negative samples based on our own results.

Modular transcriptional analyses

Module gene set information was obtained from the associated public wiki and converted to Entrez IDs with Tribe. The resulting gene sets are in data/module_genesets.

In Chiche, et al., it was demonstrated that M1.2, M3.4, and M5.12 are all associated with IFN. More specifically, M1.2 captures mostly type I IFN-inducible gene expression, whereas the other two modules are likely induced by type II IFN (though type I IFN-inducible may be captured as well).

Read in SLE WB data (Entrez IDs)

exprs.file <- file.path("data", "expression_data",
                        "SLE_WB_all_microarray_QN_zto_before.pcl")
exprs.df <- data.table::fread(exprs.file, data.table = FALSE)

Read 63.2% of 15827 rows
Read 15827 rows and 1641 (of 1641) columns from 0.418 GB file in 00:00:06

Summarize modules’ gene expression

All SLE data

# read in Tribe-converted genesets
mod.file.list <- 
  list(M1.2 = file.path("data", "module_genesets", 
                        "Chiche et al M1.2 module-6b45452.csv"),
       M3.4 = file.path("data", "module_genesets", 
                        "Chiche et al M3.4 module-7ab0d0d.csv"),
       M5.12 = file.path("data", "module_genesets", 
                         "Chiche et al M5.12 module-f326fe4.csv"))
entrez.mod.list <- lapply(mod.file.list, 
                          function(x) ReadInTribeConversions(x))
# get expression summary (mean)
mod.summary.list <- 
  lapply(entrez.mod.list, function(x) GetGeneSetMean(x, exprs.df))
# tidy
mod.summary.df <- reshape2::melt(mod.summary.list)
No id variables; using all as measure variables
No id variables; using all as measure variables
No id variables; using all as measure variables
colnames(mod.summary.df) <- c("Source Name", "Summary", "Module")
# write to file
readr::write_tsv(x = mod.summary.df, 
                 path = file.path(results.dir,
                                  "SLE-WB_Chiche_et_al_module_summary.tsv"))

Sample-data relationship

e.39088.sdrf <- data.table::fread(file.path("data", 
                                            "sample_info",
                                            "E-GEOD-39088.sdrf.txt"),
                                  data.table = FALSE)
array.att <- e.39088.sdrf[, c("Source Name",
                              "Comment [Sample_characteristics]",
                              "Characteristics [disease state]",
                              "Comment [Sample_source_name]",
                              "Comment [Sample_title]")]
# source name must match exprs.df colnames (CEL file names) in order to
# join df
array.att <-
  array.att %>%
    dplyr::mutate(`Source Name` =  gsub(" 1", "", `Source Name`))
array.att$`Source Name` <- 
  unlist(lapply(array.att$`Source Name`, 
         function(x) colnames(exprs.df)[grep(x, colnames(exprs.df))]))
colnames(array.att)[2:ncol(array.att)] <- c("Day", "Disease state", 
                                            "Treatment", "Patient")

Main

# right join, only want arrays in this data set
mod.meta.df <- 
  dplyr::right_join(mod.summary.df, array.att, by = "Source Name")
Column `Source Name` joining factor and character vector, coercing into character vector
# baseline and healthy unstimulated only
baseline.mod.df <- 
  dplyr::bind_rows(dplyr::filter(mod.meta.df, Day == "day: 0"),
                   dplyr::filter(mod.meta.df, 
                                 `Disease state` == "healthy" & !(grepl("unstimulated",
                                                                        Treatment))))
rm(mod.meta.df)
p <- ggplot2::ggplot(baseline.mod.df,
       ggplot2::aes(x = `Disease state`, y = Summary)) +
  ggplot2::geom_jitter(ggplot2::aes(colour = `Disease state`), width = 0.2) + 
  ggplot2::stat_summary(fun.y = "median", size = 4, shape = 18,
                        geom = "point", color = "black") +
  ggplot2::facet_grid(~ Module) +
  ggplot2::theme_bw() + 
  ggplot2::labs(y = "mean expression of genes in module\n(per sample)",
                title = "IFN Modular Framework Expression - Baseline",
                subtitle = "Lauwerys, et al.") +
  ggplot2::scale_color_manual(values = c("seagreen3", "#3182bd")) +
  ggplot2::theme(legend.position = "none")
p

Note that the increase in M1.2 expression was shown to be more strongly induced by IFN-beta than IFN-alpha in Chiche, et al.

# save plot
plot.file <- file.path(plot.dir,
                       "E-GEOD-39088_Chiche_et_al_baseline.pdf")
ggplot2::ggsave(plot.file, plot = p + 
                  ggplot2::theme(text = ggplot2::element_text(size = 15)))
Saving 7 x 7 in image
# which are likely the 9 IFN-negative patients in the original publication
# we don't have these labels
low.ifn.sle <- dplyr::filter(baseline.mod.df,
                             `Disease state` == "SLE") %>%
  dplyr::group_by(Module) %>%
  dplyr::top_n((Summary * -1), n = 9)
# call low samples 9 lowest M1.2 scores -- TYPE I INTERFERON
low.ifn.samples <-low.ifn.sle$`Source Name`[low.ifn.sle$Module == "M1.2"]
low.ifn.samples
[1] "GSM955819_DNA11091-067.CEL" "GSM955807_DNA11091-061.CEL" "GSM955804_DNA10204-307.CEL"
[4] "GSM955786_DNA10204-285.CEL" "GSM955783_DNA10204-281.CEL" "GSM955774_DNA10204-333.CEL"
[7] "GSM955771_DNA10204-337.CEL" "GSM955762_DNA10204-341.CEL" "GSM955759_DNA10204-309.CEL"
# remove low ifn samples that are placebo, that will be its own category
low.ifn.samples <- 
  low.ifn.samples[!grepl("Placebo", 
                         array.att$`Treatment`
                         [array.att$`Source Name` %in% low.ifn.samples])]
# get the treatment day (or timepoint information) from treatment column
# and get the patient identifier from patient column
array.att <-
  array.att %>%
    dplyr::mutate(Day = sub("^\\s+", "", sub(".*[,]", "", Treatment)),
                  Patient = sub("^\\s+", "", sub(".*[,]", "", Patient)))
# healthy controls do not have timepoint information, so replace with NA
array.att$Day[!grepl(paste(c("day", "baseline"), collapse = "|"), 
                            array.att$Day)] <- NA
# add a column that contains grouping information (IFN-K treated, placebo
# unstimulated control, stimulated control)
array.att <- 
  array.att %>%
    dplyr::mutate(Group = 
                    dplyr::case_when(
                      grepl("IFN-K", array.att$Treatment) ~ "IFN-K",
                      grepl("Placebo", array.att$Treatment) ~ "Placebo",
                      grepl("absence", array.att$Treatment) ~
                        "Control, no treatment",
                      grepl("unstimulated = ", array.att$Treatment) ~
                        "Control, stimulated"
                    ))
# which patients are in the following groups - placebo, IFN-positive, 
# IFN-negative
low.ifn.pat <- 
  array.att$Patient[which(array.att$`Source Name` %in% low.ifn.samples)]
placebo.pat <- unique(array.att$Patient[which(array.att$Group == "Placebo")])
hi.ifn.pat <- setdiff(array.att$Patient, c(low.ifn.pat, placebo.pat))
hi.ifn.pat <- hi.ifn.pat[grep("patient", hi.ifn.pat)]
array.att <-
  array.att %>% 
    dplyr::mutate(`IFN-level` = rep(NA, nrow(array.att))) %>%
    dplyr::mutate(`IFN-level` = dplyr::case_when(
      (Patient %in% low.ifn.pat) ~ "IFN-negative", 
      (Patient %in% placebo.pat) ~ "Placebo",
      (Patient %in% hi.ifn.pat) ~ "IFN-positive"
    ))
# right join, only want arrays in this data set
mod.meta.df <- 
  dplyr::right_join(mod.summary.df, array.att, by = "Source Name")
Column `Source Name` joining factor and character vector, coercing into character vector
# write to file
readr::write_tsv(mod.meta.df, 
                 path = file.path(results.dir,
                                  "E-GEOD-39088_Chiche_et_al_module.tsv"))
rm(list = setdiff(ls(), c("%>%", "mod.summary.df", "array.att",
                          "results.dir", "plot.dir")))
array.att <- dplyr::select(array.att, -`IFN-level`)

PLIER trained on SLE WB compendium

sle.b.df <- readr::read_tsv(file.path("results", "05",
                                      "SLE-WB_PLIER_B_tidy.tsv"))
Parsed with column specification:
cols(
  Sample = col_character(),
  LV = col_character(),
  Annotated = col_character(),
  Value = col_double()
)

Need to find which samples would be considered IFN-positive vs. IFN-negative using this information

ifn.b.df <- sle.b.df %>%
              dplyr::filter(LV %in% c("LV6", "LV69", "LV110")) %>%
              dplyr::right_join(y = array.att, by = c("Sample" = "Source Name"))
baseline.df <- 
  dplyr::bind_rows(dplyr::filter(ifn.b.df, Day == "baseline"),
                   dplyr::filter(ifn.b.df, 
                                 `Disease state` == "healthy" & !(grepl("unstimulated",
                                                                        Treatment))))
p <- baseline.df %>%
  ggplot2::ggplot(ggplot2::aes(x = `Disease state`, y = Value)) +
  ggplot2::geom_jitter(ggplot2::aes(colour = `Disease state`), width = 0.2) + 
  ggplot2::stat_summary(fun.y = "median", size = 4, shape = 18,
                        geom = "point", color = "black") +
  ggplot2::facet_grid(~ LV) +
  ggplot2::theme_bw() + 
  ggplot2::labs(y = "LV value",
                title = "SLE WB PLIER - Baseline",
                subtitle = "Lauwerys, et al.") +
  ggplot2::scale_color_manual(values = c("seagreen3", "#3182bd")) +
  ggplot2::theme(legend.position = "none")
p

# save plot
plot.file <- file.path(plot.dir,
                       "E-GEOD-39088_SLE-WB_PLIER_baseline.pdf")
ggplot2::ggsave(plot.file, plot = p + 
                  ggplot2::theme(text = ggplot2::element_text(size = 15)))
Saving 7 x 7 in image
# which are likely the 9 IFN-negative patients in 
low.ifn.sle <- dplyr::filter(baseline.df,
                             `Disease state` == "SLE") %>%
  dplyr::group_by(LV) %>%
  dplyr::top_n((Value * -1), n = 9) %>%
  dplyr::arrange(LV)
low.ifn.sle
table(low.ifn.sle$Patient)

 SLE patient 1 SLE patient 10 SLE patient 11 SLE patient 12 SLE patient 14 SLE patient 15  SLE patient 2 
             1              3              3              1              2              3              1 
SLE patient 21 SLE patient 22 SLE patient 25 SLE patient 26  SLE patient 6  SLE patient 7 
             2              2              1              3              3              2 

We’ll call anything that’s one of the 9 lowest samples (in more than one LV) IFN-negative for this method.

low.ifn.pat <- names(table(low.ifn.sle$Patient))[table(low.ifn.sle$Patient) > 1]
placebo.pat <- 
  unique(baseline.df$Patient[which(baseline.df$Group == "Placebo")])
# remove low IFN patients that are on placebo
low.ifn.pat <- setdiff(low.ifn.pat, placebo.pat)
hi.ifn.pat <- setdiff(array.att$Patient, c(low.ifn.pat, placebo.pat))
hi.ifn.pat <- hi.ifn.pat[grep("patient", hi.ifn.pat)]
ifn.b.df <-
  ifn.b.df %>% 
    dplyr::mutate(`IFN-level` = rep(NA, nrow(ifn.b.df))) %>%
    dplyr::mutate(`IFN-level` = dplyr::case_when(
      (Patient %in% low.ifn.pat) ~ "IFN-negative", 
      (Patient %in% placebo.pat) ~ "Placebo",
      (Patient %in% hi.ifn.pat) ~ "IFN-positive"
    )) %>%
    readr::write_tsv(path = file.path(results.dir,
                                     "E-GEOD-39088_SLE-WB_PLIER_IFN_B.tsv"))
rm(list = setdiff(ls(), c("%>%", "mod.summary.df", "array.att",
                          "sle.b.df", "plot.dir", "results.dir")))

Read in SLE B matrix (in recount2 space)

# read in B matrix
rec.b.file <- file.path("results", "07", "SLE-WB_B_matrix_recount2_model.RDS")
recount.b.mat <- as.data.frame(readRDS(rec.b.file))
recount.b.mat <- tibble::rownames_to_column(recount.b.mat, var = "Annotated")
# reshape
recount.b.df <- reshape2::melt(recount.b.mat)
Using Annotated as id variables
recount.b.df <- 
  recount.b.df %>%
    dplyr::mutate(LV = rep(paste0("LV", 1:nrow(recount.b.mat)),
                           ncol(recount.b.mat) - 1))
colnames(recount.b.df) <- c("Annotated", "Sample", "Value", "LV")
recount.b.df <- recount.b.df[, c("Sample", "LV", "Annotated", "Value")]
head(recount.b.df)
ifn.b.df <- recount.b.df %>%
              dplyr::filter(LV %in% c("LV116", "LV140")) %>%
              dplyr::right_join(y = array.att, by = c("Sample" = "Source Name"))
Column `Sample`/`Source Name` joining factor and character vector, coercing into character vector
baseline.df <- 
  dplyr::bind_rows(dplyr::filter(ifn.b.df, Day == "baseline"),
                   dplyr::filter(ifn.b.df, 
                                 `Disease state` == "healthy" & !(grepl("unstimulated",
                                                                        Treatment))))
p <- baseline.df %>%
  ggplot2::ggplot(ggplot2::aes(x = `Disease state`, y = Value)) +
  ggplot2::geom_jitter(ggplot2::aes(colour = `Disease state`), width = 0.2) + 
  ggplot2::stat_summary(fun.y = "median", size = 4, shape = 18,
                        geom = "point", color = "black") +
  ggplot2::facet_grid(~ LV) +
  ggplot2::theme_bw() + 
  ggplot2::labs(y = "LV value",
                title = "recount PLIER - Baseline",
                subtitle = "Lauwerys, et al.") +
  ggplot2::scale_color_manual(values = c("seagreen3", "#3182bd")) +
  ggplot2::theme(legend.position = "none")
p

It looks like there’s little difference between LV140 in healthy controls and patients with SLE (at baseline). This LV should capture IFN-gamma signaling (much like M3.4 and M5.12) and it shows a similar pattern of expression to M5.12.

# save plot
plot.file <- file.path(plot.dir,
                       "E-GEOD-39088_recount2_PLIER_baseline.pdf")
ggplot2::ggsave(plot.file, plot = p + 
                  ggplot2::theme(text = ggplot2::element_text(size = 15)))
Saving 7 x 7 in image

We’ll call the 9 patients with the lowest LV116 values at baseline IFN-negative in this method.

# which are likely the 9 IFN-negative patients in 
low.ifn.sle <- dplyr::filter(baseline.df,
                             `Disease state` == "SLE",
                             LV == "LV116") %>%
  dplyr::group_by(LV) %>%
  dplyr::top_n((Value * -1), n = 9)
low.ifn.pat <- low.ifn.sle$Patient
placebo.pat <- 
  unique(baseline.df$Patient[which(baseline.df$Group == "Placebo")])
# remove low IFN patients that are on placebo
low.ifn.pat <- setdiff(low.ifn.pat, placebo.pat)
hi.ifn.pat <- setdiff(array.att$Patient, c(low.ifn.pat, placebo.pat))
hi.ifn.pat <- hi.ifn.pat[grep("patient", hi.ifn.pat)]
ifn.b.df <-
  ifn.b.df %>% 
    dplyr::mutate(`IFN-level` = rep(NA, nrow(ifn.b.df))) %>%
    dplyr::mutate(`IFN-level` = dplyr::case_when(
      (Patient %in% low.ifn.pat) ~ "IFN-negative", 
      (Patient %in% placebo.pat) ~ "Placebo",
      (Patient %in% hi.ifn.pat) ~ "IFN-positive"
    )) %>%
  readr::write_tsv(path = file.path(results.dir,
                                    "E-GEOD-39088_recount2_PLIER_IFN_B.tsv"))
rm(list = setdiff(ls(), c("%>%", "mod.summary.df", "recount.b.df",
                          "sle.b.df", "plot.dir", "results.dir")))

AMG 811

E-GEOD-78193; Welcher, et al. 2015.

I’ll summarize some of the relevant points from Welcher, et al.:

  • AMG 811 is a monoclonal antibody against IFN-gamma (type II IFN).
  • Patients with SLE received either AMG 811 (different doses and/or adminstration methods) or placebo.
  • Whole blood was collected from patients with SLE at day 1 (baseline), day 15, day 56, and at the end of the study (EOS).
  • Whole blood was also collected from healthy controls and either stimulated with IFN-gamma for (0, 24, or 48 hrs) or were untreated. The authors identified genes that were differentially expressed between unstimulated and IFN-gamma stimulated samples (IFN-gamma signature). IFN-gamma scores calculated using these genes were similar to other (previously published) IFN gene sets.
  • Post-treatment (AMG811) samples showed a significant decrease in a number of the IFN-gamma signature genes

Sample-data relationship file

e.78193.sdrf <- data.table::fread(file.path("data", 
                                            "sample_info",
                                            "E-GEOD-78193.sdrf.txt"),
                                  data.table = FALSE)
array.att <- e.78193.sdrf[, c("Source Name",
                              "Comment [Sample_source_name]",
                              "Characteristics [subject]",
                              "Characteristics [treatment day]",
                              "Comment [Sample_title]")]
array.att <- 
  array.att %>%
    dplyr::mutate(Sample = sub(" 1", "", `Source Name`)) %>%
    dplyr::select(-`Source Name`)
colnames(array.att)[1:4] <- c("Disease state", "Subject", 
                              "Day", "Treatment")
# remove IFN-gamma stimulated samples, we won't be using them downstream
array.att <-
  array.att %>%
  dplyr::filter(!(grepl("+ IFN-g", Treatment))) %>%
  dplyr::select(-Treatment)  %>%
  dplyr::mutate(`Disease state` =
                  dplyr::recode(`Disease state`, 
                                "systemic lupus erythematosus (SLE) patient" = "SLE",
                                "healthy volunteer" = "healthy"))

Modular transcriptional analyses

# join sample information with the summary of the module gene sets
mod.meta.df <- dplyr::right_join(mod.summary.df, array.att, 
                                 by = c("Source Name" = "Sample"))
Column `Source Name`/`Sample` joining factor and character vector, coercing into character vector
# write to file
mod.file <- file.path(results.dir,
                      "E-GEOD-78193_Chiche_et_al_module.tsv")
readr::write_tsv(mod.meta.df, path = mod.file)
rm(mod.file, mod.meta.df)

PLIER trained on SLE WB compendium

# join sample info with SLE WB PLIER LVs
sle.b.meta.df <- dplyr::right_join(sle.b.df, array.att, by = "Sample") %>%
  dplyr::filter(LV %in% c("LV6", "LV69", "LV110"))
# write to file
sle.b.file <- file.path(results.dir, "E-GEOD-78193_SLE-WB_PLIER_IFN_B.tsv")
readr::write_tsv(sle.b.meta.df, path = sle.b.file)
rm(sle.b.meta.df)

PLIER trained on recount2

# join sample info with SLE WB PLIER LVs
rec.b.meta.df <- dplyr::right_join(recount.b.df, array.att, by = "Sample") %>%
  dplyr::filter(LV %in% c("LV116", "LV140"))
Column `Sample` joining factor and character vector, coercing into character vector
# write to file
rec.b.file <- file.path(results.dir, "E-GEOD-78193_recount2_PLIER_IFN_B.tsv")
readr::write_tsv(rec.b.meta.df, path = rec.b.file)
LS0tCnRpdGxlOiAiU0xFIElGTiBjbGluaWNhbCB0cmlhbHMgLSBkYXRhIHByZXBhcmF0aW9uIgpvdXRwdXQ6ICAgCiAgaHRtbF9ub3RlYm9vazogCiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQotLS0KCk91ciBTTEUgV0IgY29tcGVuZGl1bSBpbmNsdWRlcyBbYSB0cmlhbCBvZiBpbnRlcmZlcm9uLWFscGhhLWtpbm9pZCAoSUZOLUspXShodHRwczovL2RvaS5vcmcvMTAuMTAwMi9hcnQuMzc3ODUpLCAKd2hpY2ggc2hvdWxkIGJsb2NrIHR5cGUgSSBJRk4gb25seSBhbmQgdGhlcmVmb3JlIHRoZSBleHByZXNzaW9uIGxldmVscyBvZiAKSUZOLWFscGhhL0lGTi1iZXRhIGdlbmUgc2lnbmF0dXJlcyBzaG91bGQgZGVjcmVhc2UgZHVyaW5nIHRyZWF0bWVudCAobGVhdmluZyAKSUZOLWdhbW1hIHNpZ25hdHVyZSBleHByZXNzaW9uIHJlbGF0aXZlbHkgdW5jaGFuZ2VkKS4gCltBIHRyaWFsIG9mIEFNRy04MTFdKGh0dHBzOi8vd3d3Lm5jYmkubmxtLm5paC5nb3YvcG1jL2FydGljbGVzL1BNQzUwNTQ5MzUvKSwgCndoaWNoIGlzIGEgbW9ub2Nsb25hbCBhbnRpYm9keSBhZ2FpbnN0IElGTi1nYW1tYSwgaXMgYWxzbyBpbmNsdWRlZC4gV2UgZXhwZWN0ZWQgCnRvIHNlZSBJRk4tZ2FtbWEgZ2VuZSBleHByZXNzaW9uIGRlY3JlYXNlIGluIHRoZSBBTUctODExIGR1cmluZyB0cmVhdG1lbnQuCgpJbiB0aGlzIG5vdGVib29rLCB3ZSdsbCB0aWR5IGRhdGEgaW4gcHJlcGFyYXRpb24gZm9yIGFuYWx5emluZyBjaGFuZ2VzIGluIApJRk4tcmVsYXRlZCBnZW5lIGV4cHJlc3Npb24gdXNpbmcgYSBjb3VwbGUgZGlmZmVyZW50IGdlbmUgc2V0cyBvciBtb2RlbHMgLS0KbW9kdWxhciB0cmFuc2NyaXB0aW9uYWwgYW5hbHlzZXMgKGZyb20gCltDaGljaGUsIGV0IGFsLiAyMDE0Ll0oaHR0cHM6Ly93d3cubmNiaS5ubG0ubmloLmdvdi9wbWMvYXJ0aWNsZXMvUE1DNDE1NzgyNi8pKSwgCmEgUExJRVIgbW9kZWwgdHJhaW5lZCBvbiB0aGUgU0xFIFdCIGNvbXBlbmRpdW0gYW5kIGEgUExJRVIgbW9kZWwgdHJhaW5lZCBvbiAKcmVjb3VudDIgZGF0YS4KCkZvciBtb3JlIGluZm9ybWF0aW9uIGFib3V0IHRoZSB0eXBlcyBvZiBJRk5zLCBzZWUgdGhlIGludHJvIHRvIApgMDgtaWRlbnRpZnlfaWZuX0xWc2AuCgojIyBGdW5jdGlvbnMgYW5kIGRpcmVjdG9yeSBzZXQgdXAKCmBgYHtyfQpgJT4lYCA8LSBkcGx5cjo6YCU+JWAKYGBgCgojIyMjIEZ1bmN0aW9ucyBzcGVjaWZpY2FsbHkgZm9yIG1vZHVsYXIgYW5hbHlzZXMKYGBge3J9ClJlYWRJblRyaWJlQ29udmVyc2lvbnMgPC0gZnVuY3Rpb24oZmlsZW5hbWUpIHsKICAjIFJlYWRzIGluIGNzdiBmaWxlIGRvd25sb2FkZWQgZnJvbSBUcmliZSBhbmQgcmV0dXJucyBjaGFyYWN0ZXIgdmVjdG9yLiAKICAjIFRoZSBnZW5lIGlkZW50aWZpZXJzIHN0YXJ0IGF0IHRoZSA3dGggbGluZS4gCiAgIwogICMgQXJnczoKICAjICAgZmlsZW5hbWU6IGZ1bGwgcGF0aCB0byBjc3YgZmlsZSBjb250YWluaW5nIHRoZSBsaXN0IG9mIGdlbmUgaWRlbnRpZmVycwogICMKICAjIFJldHVybnM6CiAgIyAgIGdlbmVzZXQ6IGEgdmVjdG9yIChjaGFyYWN0ZXIpIG9mIGdlbmUgaWRlbnRpZmllcnMKICAjCiAgCiAgZ2VuZXNldCA8LSByZWFkTGluZXMoZmlsZW5hbWUpCiAgZ2VuZXNldCA8LSBnZW5lc2V0Wzc6bGVuZ3RoKGdlbmVzZXQpXQogIGdlbmVzZXQgPC0gZ3N1YigiXHQiLCAiIiwgZ2VuZXNldCkKICByZXR1cm4oZ2VuZXNldCkKCn0KCkdldEdlbmVTZXRNZWFuIDwtIGZ1bmN0aW9uKGdlbmUuc2V0LCBleHBycykgewogICMgU3VtbWFyaXplIHRoZSBleHByZXNzaW9uIGxldmVscyBvZiBnZW5lcyBpbiBhIGdlbmUgc2V0IGJ5IHRha2luZyB0aGUgbWVhbgogICMgZXhwcmVzc2lvbiB2YWx1ZSBvZiBhbGwgZ2VuZXMgaW4gdGhhdCBnZW5lIHNldAogICMKICAjIEFyZ3M6CiAgIyAgIGdlbmUuc2V0OiB2ZWN0b3Igb2YgZ2VuZSBpZGVudGlmaWVycyAtLSBjb3JyZXNwb25kcyB0byB0aGUgZ2VuZXNldCB0byBiZQogICMgICAgICAgICAgICAgc3VtbWFyaXplZAogICMgICBleHByczogZGF0YS5mcmFtZSB0aGF0IGNvbnRhaW5zIHRoZSBleHByZXNzaW9uIHZhbHVlcyAocm93cyBhcmUgZ2VuZXMsCiAgIyAgICAgICAgICBjb2x1bW5zIGFyZSBzYW1wbGVzKTsgZmlyc3QgY29sdW1uIC0tICJHZW5lIiAtLSBjb250YWlucyBnZW5lIGlkcwogICMgICAgICAgICAgKG1hdGNoIHRoZSB0eXBlIG9mIGlkZW50aWZpZXJzIHVzZWQgaW4gZ2VuZS5zZXQpCiAgIwogICMgUmV0dXJuczoKICAjICAgc3VtbWFyeS5kZjogYSBkYXRhLmZyYW1lLCAxIHJvdyBjb3JyZXNwb25kaW5nIHRvIHRoZSBtZWFuIGV4cHJlc3Npb24gCiAgIyAgICAgICAgICAgICAgIHZhbHVlcyBmb3IgZ2VuZXMgaW4gZ2VuZS5zZXQsIGNvbHVtbnMgYXJlIHNhbXBsZXMKICAjCiAgCiAgaWYgKGNvbG5hbWVzKGV4cHJzKVsxXSAhPSAiR2VuZSIpIHsKICAgIHN0b3AoIlRoZSBmaXJzdCBjb2x1bW4gbXVzdCBjb250YWluIGdlbmUgaWRlbnRpZmllcnMgYW5kIGhhdmUKICAgICAgICAgdGhlIGNvbG5hbWUgJ0dlbmUnIikKICB9CiAgCiAgYCU+JWAgPC0gZHBseXI6OmAlPiVgCgogIHN1bW1hcnkuZGYgPC0gZXhwcnMgJT4lCiAgICAgICAgICAgICAgICAgIGRwbHlyOjpmaWx0ZXIoR2VuZSAlaW4lIGdlbmUuc2V0KSAlPiUgCiAgICAgICAgICAgICAgICAgIGRwbHlyOjpzZWxlY3QoLUdlbmUpICU+JQogICAgICAgICAgICAgICAgICBkcGx5cjo6c3VtbWFyaXNlX2FsbChtZWFuKQogIAogIHJldHVybihzdW1tYXJ5LmRmKQogIAp9CmBgYAoKYGBge3J9CiMgcGxvdCBhbmQgcmVzdWx0IGRpcmVjdG9yeSBzZXR1cCBmb3IgdGhpcyBub3RlYm9vawpwbG90LmRpciA8LSBmaWxlLnBhdGgoInBsb3RzIiwgIjA5IikKZGlyLmNyZWF0ZShwbG90LmRpciwgcmVjdXJzaXZlID0gVFJVRSwgc2hvd1dhcm5pbmdzID0gRkFMU0UpCnJlc3VsdHMuZGlyIDwtIGZpbGUucGF0aCgicmVzdWx0cyIsICIwOSIpCmRpci5jcmVhdGUocmVzdWx0cy5kaXIsIHJlY3Vyc2l2ZSA9IFRSVUUsIHNob3dXYXJuaW5ncyA9IEZBTFNFKQpgYGAKCmBgYHtyfQojIHNldCBzZWVkIGZvciByZXByb2R1Y2liaWxpdHkgKHBsb3Qgaml0dGVyKQpzZXQuc2VlZCgxMjMpCmBgYAoKCiMjIElGTi1LCgoqKkUtR0VPRC0zOTA4ODsgTGF1d2VyeXMsIGV0IGFsLiAyMDEzLioqCgpGaXJzdCwgSSdsbCBzdW1tYXJpemUgc29tZSByZWxldmFudCBwb2ludHMgZnJvbSBMYXV3ZXJ5cywgZXQgYWwuOgoKKiBJRk4tSyBpcyBhIHRoZXJhcGV1dGljIHZhY2NpbmUuIApJdCBpbmR1Y2VzIElGTi1hbHBoYSBhbnRpYm9kaWVzIGluIHRob3NlIHdobyByZWNlaXZlIGl0LgpJbiB0dXJuLCB0aG9zZSBhbnRpYm9kaWVzIHdpbGwgYmluZCBJRk4tYWxwaGEgYW5kIHRoZXJlZm9yZSByZWR1Y2UgaXRzIAphY3Rpdml0eS9hYmlsaXR5IHRvIHN0aW11bGF0ZSB0aGUgaW1tdW5lIHN5c3RlbS4KKiBQYXRpZW50cyB3aXRoIFNMRSB3ZXJlIHRyZWF0ZWQgd2l0aCBvbmUgb2YgZm91ciBkb3NlcyBvZiBJRk4tSyBvciBwbGFjZWJvLgoqIFdob2xlIGJsb29kIHdhcyBjb2xsZWN0ZWQgZnJvbSBwYXRpZW50cyBhdCBkYXlzIDAgKGJhc2VsaW5lKSwgMTEyLCBhbmQgMTY4LgoqIEhlYWx0aHkgY29udHJvbCBibG9vZCB3YXMgYWxzbyBpbmNsdWRlZCAoMiBzYW1wbGVzIGZyb20gZWFjaCBjb250cm9sKTsgCm9uZSBvZiB0aGVzZSBoZWFsdGh5IGNvbnRyb2wgYmxvb2Qgc2FtcGxlcyB3YXMgdHJlYXRlZCB3aXRoIHR5cGUgSSBJRk5zIAooSUZOLWFscGhhIHN1YnR5cGUocykgc3BlY2lmaWNhbGx5KSB0byBkZXJpdmUgYW4gSUZOLWluZHVjaWJsZSBnZW5lIGV4cHJlc3Npb24gCnNpZ25hdHVyZS4KKiBUaGUgYXV0aG9ycyBzdHJhdGlmaWVkIHBhdGllbnRzIGludG8gX0lGTi1wb3NpdGl2ZV8gKG4gPSAxOCkgYW5kIApfSUZOLW5lZ2F0aXZlXyAobiA9IDkpIGdyb3VwcyBiYXNlZCBvbiB0aGUgZXhwcmVzc2lvbiBsZXZlbHMgb2YgdGhlIApJRk4taW5kdWNpYmxlIGdlbmUgc2lnbmF0dXJlIGF0IGJhc2VsaW5lLgoqIFBhdGllbnRzIHdpdGggYSBJRk4tcG9zaXRpdmUgYmFzZWxpbmUgc2lnbmF0dXJlIGhhZCBhIHJlZHVjdGlvbiBpbiAKSUZOLWluZHVjaWJsZSB0cmVhdG1lbnQgZHVyaW5nIHRyZWF0bWVudC4gCgpXZSBkbyBub3QgaGF2ZSBpbmZvcm1hdGlvbiBhYm91dCB3aGljaCBwYXRpZW50cyBhcmUgaW4gd2hpY2ggb2YgdGhlc2UgdHdvIApncm91cHMsIHNvIHdlJ2xsIGhhdmUgdG8gZGVzaWduYXRlIElGTi1wb3NpdGl2ZSBhbmQgSUZOLW5lZ2F0aXZlIHNhbXBsZXMgYmFzZWQgCm9uIG91ciBvd24gcmVzdWx0cy4KCiMjIyBNb2R1bGFyIHRyYW5zY3JpcHRpb25hbCBhbmFseXNlcwoKTW9kdWxlIGdlbmUgc2V0IGluZm9ybWF0aW9uIHdhcyBvYnRhaW5lZCBmcm9tIApbdGhlIGFzc29jaWF0ZWQgcHVibGljIHdpa2ldKGh0dHBzOi8vd3d3LmJpaXIubmV0L3B1YmxpY193aWtpcy9tb2R1bGVfYW5ub3RhdGlvbi9WMl9UcmlhbF84X01vZHVsZXMpIAphbmQgY29udmVydGVkIHRvIEVudHJleiBJRHMgd2l0aCBbVHJpYmVdKGh0dHA6Ly90cmliZS5ncmVlbmVsYWIuY29tLykuIApUaGUgcmVzdWx0aW5nIGdlbmUgc2V0cyBhcmUgaW4gYGRhdGEvbW9kdWxlX2dlbmVzZXRzYC4KCkluIENoaWNoZSwgZXQgYWwuLCBpdCB3YXMgZGVtb25zdHJhdGVkIHRoYXQgYE0xLjJgLCBgTTMuNGAsIGFuZCBgTTUuMTJgIGFyZSBhbGwgCmFzc29jaWF0ZWQgd2l0aCBJRk4uCk1vcmUgc3BlY2lmaWNhbGx5LCBgTTEuMmAgY2FwdHVyZXMgbW9zdGx5IHR5cGUgSSBJRk4taW5kdWNpYmxlIGdlbmUgZXhwcmVzc2lvbiwgCndoZXJlYXMgdGhlIG90aGVyIHR3byBtb2R1bGVzIGFyZSBsaWtlbHkgaW5kdWNlZCBieSB0eXBlIElJIElGTiAodGhvdWdoIHR5cGUgSSAKSUZOLWluZHVjaWJsZSBtYXkgYmUgY2FwdHVyZWQgYXMgd2VsbCkuCgoKIyMjIyBSZWFkIGluIFNMRSBXQiBkYXRhIChFbnRyZXogSURzKQoKYGBge3J9CmV4cHJzLmZpbGUgPC0gZmlsZS5wYXRoKCJkYXRhIiwgImV4cHJlc3Npb25fZGF0YSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJTTEVfV0JfYWxsX21pY3JvYXJyYXlfUU5fenRvX2JlZm9yZS5wY2wiKQpleHBycy5kZiA8LSBkYXRhLnRhYmxlOjpmcmVhZChleHBycy5maWxlLCBkYXRhLnRhYmxlID0gRkFMU0UpCmBgYAoKIyMjIyBTdW1tYXJpemUgbW9kdWxlcycgZ2VuZSBleHByZXNzaW9uCgpBbGwgU0xFIGRhdGEKCmBgYHtyfQojIHJlYWQgaW4gVHJpYmUtY29udmVydGVkIGdlbmVzZXRzCm1vZC5maWxlLmxpc3QgPC0gCiAgbGlzdChNMS4yID0gZmlsZS5wYXRoKCJkYXRhIiwgIm1vZHVsZV9nZW5lc2V0cyIsIAogICAgICAgICAgICAgICAgICAgICAgICAiQ2hpY2hlIGV0IGFsIE0xLjIgbW9kdWxlLTZiNDU0NTIuY3N2IiksCiAgICAgICBNMy40ID0gZmlsZS5wYXRoKCJkYXRhIiwgIm1vZHVsZV9nZW5lc2V0cyIsIAogICAgICAgICAgICAgICAgICAgICAgICAiQ2hpY2hlIGV0IGFsIE0zLjQgbW9kdWxlLTdhYjBkMGQuY3N2IiksCiAgICAgICBNNS4xMiA9IGZpbGUucGF0aCgiZGF0YSIsICJtb2R1bGVfZ2VuZXNldHMiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICJDaGljaGUgZXQgYWwgTTUuMTIgbW9kdWxlLWYzMjZmZTQuY3N2IikpCgplbnRyZXoubW9kLmxpc3QgPC0gbGFwcGx5KG1vZC5maWxlLmxpc3QsIAogICAgICAgICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uKHgpIFJlYWRJblRyaWJlQ29udmVyc2lvbnMoeCkpCmBgYAoKYGBge3J9CiMgZ2V0IGV4cHJlc3Npb24gc3VtbWFyeSAobWVhbikKbW9kLnN1bW1hcnkubGlzdCA8LSAKICBsYXBwbHkoZW50cmV6Lm1vZC5saXN0LCBmdW5jdGlvbih4KSBHZXRHZW5lU2V0TWVhbih4LCBleHBycy5kZikpCgojIHRpZHkKbW9kLnN1bW1hcnkuZGYgPC0gcmVzaGFwZTI6Om1lbHQobW9kLnN1bW1hcnkubGlzdCkKY29sbmFtZXMobW9kLnN1bW1hcnkuZGYpIDwtIGMoIlNvdXJjZSBOYW1lIiwgIlN1bW1hcnkiLCAiTW9kdWxlIikKCiMgd3JpdGUgdG8gZmlsZQpyZWFkcjo6d3JpdGVfdHN2KHggPSBtb2Quc3VtbWFyeS5kZiwgCiAgICAgICAgICAgICAgICAgcGF0aCA9IGZpbGUucGF0aChyZXN1bHRzLmRpciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTTEUtV0JfQ2hpY2hlX2V0X2FsX21vZHVsZV9zdW1tYXJ5LnRzdiIpKQpgYGAKCiMjIyMgU2FtcGxlLWRhdGEgcmVsYXRpb25zaGlwCgpgYGB7cn0KZS4zOTA4OC5zZHJmIDwtIGRhdGEudGFibGU6OmZyZWFkKGZpbGUucGF0aCgiZGF0YSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzYW1wbGVfaW5mbyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkUtR0VPRC0zOTA4OC5zZHJmLnR4dCIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YS50YWJsZSA9IEZBTFNFKQphcnJheS5hdHQgPC0gZS4zOTA4OC5zZHJmWywgYygiU291cmNlIE5hbWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ29tbWVudCBbU2FtcGxlX2NoYXJhY3RlcmlzdGljc10iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ2hhcmFjdGVyaXN0aWNzIFtkaXNlYXNlIHN0YXRlXSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDb21tZW50IFtTYW1wbGVfc291cmNlX25hbWVdIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNvbW1lbnQgW1NhbXBsZV90aXRsZV0iKV0KCiMgc291cmNlIG5hbWUgbXVzdCBtYXRjaCBleHBycy5kZiBjb2xuYW1lcyAoQ0VMIGZpbGUgbmFtZXMpIGluIG9yZGVyIHRvCiMgam9pbiBkZgphcnJheS5hdHQgPC0KICBhcnJheS5hdHQgJT4lCiAgICBkcGx5cjo6bXV0YXRlKGBTb3VyY2UgTmFtZWAgPSAgZ3N1YigiIDEiLCAiIiwgYFNvdXJjZSBOYW1lYCkpCmFycmF5LmF0dCRgU291cmNlIE5hbWVgIDwtIAogIHVubGlzdChsYXBwbHkoYXJyYXkuYXR0JGBTb3VyY2UgTmFtZWAsIAogICAgICAgICBmdW5jdGlvbih4KSBjb2xuYW1lcyhleHBycy5kZilbZ3JlcCh4LCBjb2xuYW1lcyhleHBycy5kZikpXSkpCmNvbG5hbWVzKGFycmF5LmF0dClbMjpuY29sKGFycmF5LmF0dCldIDwtIGMoIkRheSIsICJEaXNlYXNlIHN0YXRlIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlRyZWF0bWVudCIsICJQYXRpZW50IikKYGBgCgojIyMjIE1haW4KCmBgYHtyfQojIHJpZ2h0IGpvaW4sIG9ubHkgd2FudCBhcnJheXMgaW4gdGhpcyBkYXRhIHNldAptb2QubWV0YS5kZiA8LSAKICBkcGx5cjo6cmlnaHRfam9pbihtb2Quc3VtbWFyeS5kZiwgYXJyYXkuYXR0LCBieSA9ICJTb3VyY2UgTmFtZSIpCgojIGJhc2VsaW5lIGFuZCBoZWFsdGh5IHVuc3RpbXVsYXRlZCBvbmx5CmJhc2VsaW5lLm1vZC5kZiA8LSAKICBkcGx5cjo6YmluZF9yb3dzKGRwbHlyOjpmaWx0ZXIobW9kLm1ldGEuZGYsIERheSA9PSAiZGF5OiAwIiksCiAgICAgICAgICAgICAgICAgICBkcGx5cjo6ZmlsdGVyKG1vZC5tZXRhLmRmLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYERpc2Vhc2Ugc3RhdGVgID09ICJoZWFsdGh5IiAmICEoZ3JlcGwoInVuc3RpbXVsYXRlZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyZWF0bWVudCkpKSkKcm0obW9kLm1ldGEuZGYpCmBgYApgYGB7cn0KcCA8LSBnZ3Bsb3QyOjpnZ3Bsb3QoYmFzZWxpbmUubW9kLmRmLAogICAgICAgZ2dwbG90Mjo6YWVzKHggPSBgRGlzZWFzZSBzdGF0ZWAsIHkgPSBTdW1tYXJ5KSkgKwogIGdncGxvdDI6Omdlb21faml0dGVyKGdncGxvdDI6OmFlcyhjb2xvdXIgPSBgRGlzZWFzZSBzdGF0ZWApLCB3aWR0aCA9IDAuMikgKyAKICBnZ3Bsb3QyOjpzdGF0X3N1bW1hcnkoZnVuLnkgPSAibWVkaWFuIiwgc2l6ZSA9IDQsIHNoYXBlID0gMTgsCiAgICAgICAgICAgICAgICAgICAgICAgIGdlb20gPSAicG9pbnQiLCBjb2xvciA9ICJibGFjayIpICsKICBnZ3Bsb3QyOjpmYWNldF9ncmlkKH4gTW9kdWxlKSArCiAgZ2dwbG90Mjo6dGhlbWVfYncoKSArIAogIGdncGxvdDI6OmxhYnMoeSA9ICJtZWFuIGV4cHJlc3Npb24gb2YgZ2VuZXMgaW4gbW9kdWxlXG4ocGVyIHNhbXBsZSkiLAogICAgICAgICAgICAgICAgdGl0bGUgPSAiSUZOIE1vZHVsYXIgRnJhbWV3b3JrIEV4cHJlc3Npb24gLSBCYXNlbGluZSIsCiAgICAgICAgICAgICAgICBzdWJ0aXRsZSA9ICJMYXV3ZXJ5cywgZXQgYWwuIikgKwogIGdncGxvdDI6OnNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJzZWFncmVlbjMiLCAiIzMxODJiZCIpKSArCiAgZ2dwbG90Mjo6dGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpwCmBgYAoKTm90ZSB0aGF0IHRoZSBpbmNyZWFzZSBpbiBgTTEuMmAgZXhwcmVzc2lvbiB3YXMgc2hvd24gdG8gYmUgbW9yZSBzdHJvbmdseSAKaW5kdWNlZCBieSBJRk4tYmV0YSB0aGFuIElGTi1hbHBoYSBpbiBDaGljaGUsIGV0IGFsLgoKYGBge3J9CiMgc2F2ZSBwbG90CnBsb3QuZmlsZSA8LSBmaWxlLnBhdGgocGxvdC5kaXIsCiAgICAgICAgICAgICAgICAgICAgICAgIkUtR0VPRC0zOTA4OF9DaGljaGVfZXRfYWxfYmFzZWxpbmUucGRmIikKZ2dwbG90Mjo6Z2dzYXZlKHBsb3QuZmlsZSwgcGxvdCA9IHAgKyAKICAgICAgICAgICAgICAgICAgZ2dwbG90Mjo6dGhlbWUodGV4dCA9IGdncGxvdDI6OmVsZW1lbnRfdGV4dChzaXplID0gMTUpKSkKYGBgCgpgYGB7cn0KIyB3aGljaCBhcmUgbGlrZWx5IHRoZSA5IElGTi1uZWdhdGl2ZSBwYXRpZW50cyBpbiB0aGUgb3JpZ2luYWwgcHVibGljYXRpb24KIyB3ZSBkb24ndCBoYXZlIHRoZXNlIGxhYmVscwpsb3cuaWZuLnNsZSA8LSBkcGx5cjo6ZmlsdGVyKGJhc2VsaW5lLm1vZC5kZiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgRGlzZWFzZSBzdGF0ZWAgPT0gIlNMRSIpICU+JQogIGRwbHlyOjpncm91cF9ieShNb2R1bGUpICU+JQogIGRwbHlyOjp0b3BfbigoU3VtbWFyeSAqIC0xKSwgbiA9IDkpCgojIGNhbGwgbG93IHNhbXBsZXMgOSBsb3dlc3QgTTEuMiBzY29yZXMgLS0gVFlQRSBJIElOVEVSRkVST04KbG93Lmlmbi5zYW1wbGVzIDwtbG93Lmlmbi5zbGUkYFNvdXJjZSBOYW1lYFtsb3cuaWZuLnNsZSRNb2R1bGUgPT0gIk0xLjIiXQpsb3cuaWZuLnNhbXBsZXMKYGBgCgpgYGB7cn0KIyByZW1vdmUgbG93IGlmbiBzYW1wbGVzIHRoYXQgYXJlIHBsYWNlYm8sIHRoYXQgd2lsbCBiZSBpdHMgb3duIGNhdGVnb3J5Cmxvdy5pZm4uc2FtcGxlcyA8LSAKICBsb3cuaWZuLnNhbXBsZXNbIWdyZXBsKCJQbGFjZWJvIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICBhcnJheS5hdHQkYFRyZWF0bWVudGAKICAgICAgICAgICAgICAgICAgICAgICAgIFthcnJheS5hdHQkYFNvdXJjZSBOYW1lYCAlaW4lIGxvdy5pZm4uc2FtcGxlc10pXQoKIyBnZXQgdGhlIHRyZWF0bWVudCBkYXkgKG9yIHRpbWVwb2ludCBpbmZvcm1hdGlvbikgZnJvbSB0cmVhdG1lbnQgY29sdW1uCiMgYW5kIGdldCB0aGUgcGF0aWVudCBpZGVudGlmaWVyIGZyb20gcGF0aWVudCBjb2x1bW4KYXJyYXkuYXR0IDwtCiAgYXJyYXkuYXR0ICU+JQogICAgZHBseXI6Om11dGF0ZShEYXkgPSBzdWIoIl5cXHMrIiwgIiIsIHN1YigiLipbLF0iLCAiIiwgVHJlYXRtZW50KSksCiAgICAgICAgICAgICAgICAgIFBhdGllbnQgPSBzdWIoIl5cXHMrIiwgIiIsIHN1YigiLipbLF0iLCAiIiwgUGF0aWVudCkpKQojIGhlYWx0aHkgY29udHJvbHMgZG8gbm90IGhhdmUgdGltZXBvaW50IGluZm9ybWF0aW9uLCBzbyByZXBsYWNlIHdpdGggTkEKYXJyYXkuYXR0JERheVshZ3JlcGwocGFzdGUoYygiZGF5IiwgImJhc2VsaW5lIiksIGNvbGxhcHNlID0gInwiKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcnJheS5hdHQkRGF5KV0gPC0gTkEKCiMgYWRkIGEgY29sdW1uIHRoYXQgY29udGFpbnMgZ3JvdXBpbmcgaW5mb3JtYXRpb24gKElGTi1LIHRyZWF0ZWQsIHBsYWNlYm8KIyB1bnN0aW11bGF0ZWQgY29udHJvbCwgc3RpbXVsYXRlZCBjb250cm9sKQphcnJheS5hdHQgPC0gCiAgYXJyYXkuYXR0ICU+JQogICAgZHBseXI6Om11dGF0ZShHcm91cCA9IAogICAgICAgICAgICAgICAgICAgIGRwbHlyOjpjYXNlX3doZW4oCiAgICAgICAgICAgICAgICAgICAgICBncmVwbCgiSUZOLUsiLCBhcnJheS5hdHQkVHJlYXRtZW50KSB+ICJJRk4tSyIsCiAgICAgICAgICAgICAgICAgICAgICBncmVwbCgiUGxhY2VibyIsIGFycmF5LmF0dCRUcmVhdG1lbnQpIH4gIlBsYWNlYm8iLAogICAgICAgICAgICAgICAgICAgICAgZ3JlcGwoImFic2VuY2UiLCBhcnJheS5hdHQkVHJlYXRtZW50KSB+CiAgICAgICAgICAgICAgICAgICAgICAgICJDb250cm9sLCBubyB0cmVhdG1lbnQiLAogICAgICAgICAgICAgICAgICAgICAgZ3JlcGwoInVuc3RpbXVsYXRlZCA9ICIsIGFycmF5LmF0dCRUcmVhdG1lbnQpIH4KICAgICAgICAgICAgICAgICAgICAgICAgIkNvbnRyb2wsIHN0aW11bGF0ZWQiCiAgICAgICAgICAgICAgICAgICAgKSkKCiMgd2hpY2ggcGF0aWVudHMgYXJlIGluIHRoZSBmb2xsb3dpbmcgZ3JvdXBzIC0gcGxhY2VibywgSUZOLXBvc2l0aXZlLCAKIyBJRk4tbmVnYXRpdmUKbG93Lmlmbi5wYXQgPC0gCiAgYXJyYXkuYXR0JFBhdGllbnRbd2hpY2goYXJyYXkuYXR0JGBTb3VyY2UgTmFtZWAgJWluJSBsb3cuaWZuLnNhbXBsZXMpXQpwbGFjZWJvLnBhdCA8LSB1bmlxdWUoYXJyYXkuYXR0JFBhdGllbnRbd2hpY2goYXJyYXkuYXR0JEdyb3VwID09ICJQbGFjZWJvIildKQpoaS5pZm4ucGF0IDwtIHNldGRpZmYoYXJyYXkuYXR0JFBhdGllbnQsIGMobG93Lmlmbi5wYXQsIHBsYWNlYm8ucGF0KSkKaGkuaWZuLnBhdCA8LSBoaS5pZm4ucGF0W2dyZXAoInBhdGllbnQiLCBoaS5pZm4ucGF0KV0KCmFycmF5LmF0dCA8LQogIGFycmF5LmF0dCAlPiUgCiAgICBkcGx5cjo6bXV0YXRlKGBJRk4tbGV2ZWxgID0gcmVwKE5BLCBucm93KGFycmF5LmF0dCkpKSAlPiUKICAgIGRwbHlyOjptdXRhdGUoYElGTi1sZXZlbGAgPSBkcGx5cjo6Y2FzZV93aGVuKAogICAgICAoUGF0aWVudCAlaW4lIGxvdy5pZm4ucGF0KSB+ICJJRk4tbmVnYXRpdmUiLCAKICAgICAgKFBhdGllbnQgJWluJSBwbGFjZWJvLnBhdCkgfiAiUGxhY2VibyIsCiAgICAgIChQYXRpZW50ICVpbiUgaGkuaWZuLnBhdCkgfiAiSUZOLXBvc2l0aXZlIgogICAgKSkKCiMgcmlnaHQgam9pbiwgb25seSB3YW50IGFycmF5cyBpbiB0aGlzIGRhdGEgc2V0Cm1vZC5tZXRhLmRmIDwtIAogIGRwbHlyOjpyaWdodF9qb2luKG1vZC5zdW1tYXJ5LmRmLCBhcnJheS5hdHQsIGJ5ID0gIlNvdXJjZSBOYW1lIikKCiMgd3JpdGUgdG8gZmlsZQpyZWFkcjo6d3JpdGVfdHN2KG1vZC5tZXRhLmRmLCAKICAgICAgICAgICAgICAgICBwYXRoID0gZmlsZS5wYXRoKHJlc3VsdHMuZGlyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkUtR0VPRC0zOTA4OF9DaGljaGVfZXRfYWxfbW9kdWxlLnRzdiIpKQpgYGAKCmBgYHtyfQpybShsaXN0ID0gc2V0ZGlmZihscygpLCBjKCIlPiUiLCAibW9kLnN1bW1hcnkuZGYiLCAiYXJyYXkuYXR0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAicmVzdWx0cy5kaXIiLCAicGxvdC5kaXIiKSkpCmFycmF5LmF0dCA8LSBkcGx5cjo6c2VsZWN0KGFycmF5LmF0dCwgLWBJRk4tbGV2ZWxgKQpgYGAKCgojIyMgUExJRVIgdHJhaW5lZCBvbiBTTEUgV0IgY29tcGVuZGl1bQoKYGBge3J9CnNsZS5iLmRmIDwtIHJlYWRyOjpyZWFkX3RzdihmaWxlLnBhdGgoInJlc3VsdHMiLCAiMDUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTTEUtV0JfUExJRVJfQl90aWR5LnRzdiIpKQpgYGAKCk5lZWQgdG8gZmluZCB3aGljaCBzYW1wbGVzIHdvdWxkIGJlIGNvbnNpZGVyZWQgSUZOLXBvc2l0aXZlIHZzLiAKSUZOLW5lZ2F0aXZlIHVzaW5nIHRoaXMgaW5mb3JtYXRpb24KYGBge3J9Cmlmbi5iLmRmIDwtIHNsZS5iLmRmICU+JQogICAgICAgICAgICAgIGRwbHlyOjpmaWx0ZXIoTFYgJWluJSBjKCJMVjYiLCAiTFY2OSIsICJMVjExMCIpKSAlPiUKICAgICAgICAgICAgICBkcGx5cjo6cmlnaHRfam9pbih5ID0gYXJyYXkuYXR0LCBieSA9IGMoIlNhbXBsZSIgPSAiU291cmNlIE5hbWUiKSkKCmJhc2VsaW5lLmRmIDwtIAogIGRwbHlyOjpiaW5kX3Jvd3MoZHBseXI6OmZpbHRlcihpZm4uYi5kZiwgRGF5ID09ICJiYXNlbGluZSIpLAogICAgICAgICAgICAgICAgICAgZHBseXI6OmZpbHRlcihpZm4uYi5kZiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGBEaXNlYXNlIHN0YXRlYCA9PSAiaGVhbHRoeSIgJiAhKGdyZXBsKCJ1bnN0aW11bGF0ZWQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmVhdG1lbnQpKSkpCmBgYAoKYGBge3J9CnAgPC0gYmFzZWxpbmUuZGYgJT4lCiAgZ2dwbG90Mjo6Z2dwbG90KGdncGxvdDI6OmFlcyh4ID0gYERpc2Vhc2Ugc3RhdGVgLCB5ID0gVmFsdWUpKSArCiAgZ2dwbG90Mjo6Z2VvbV9qaXR0ZXIoZ2dwbG90Mjo6YWVzKGNvbG91ciA9IGBEaXNlYXNlIHN0YXRlYCksIHdpZHRoID0gMC4yKSArIAogIGdncGxvdDI6OnN0YXRfc3VtbWFyeShmdW4ueSA9ICJtZWRpYW4iLCBzaXplID0gNCwgc2hhcGUgPSAxOCwKICAgICAgICAgICAgICAgICAgICAgICAgZ2VvbSA9ICJwb2ludCIsIGNvbG9yID0gImJsYWNrIikgKwogIGdncGxvdDI6OmZhY2V0X2dyaWQofiBMVikgKwogIGdncGxvdDI6OnRoZW1lX2J3KCkgKyAKICBnZ3Bsb3QyOjpsYWJzKHkgPSAiTFYgdmFsdWUiLAogICAgICAgICAgICAgICAgdGl0bGUgPSAiU0xFIFdCIFBMSUVSIC0gQmFzZWxpbmUiLAogICAgICAgICAgICAgICAgc3VidGl0bGUgPSAiTGF1d2VyeXMsIGV0IGFsLiIpICsKICBnZ3Bsb3QyOjpzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygic2VhZ3JlZW4zIiwgIiMzMTgyYmQiKSkgKwogIGdncGxvdDI6OnRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKcApgYGAKCmBgYHtyfQojIHNhdmUgcGxvdApwbG90LmZpbGUgPC0gZmlsZS5wYXRoKHBsb3QuZGlyLAogICAgICAgICAgICAgICAgICAgICAgICJFLUdFT0QtMzkwODhfU0xFLVdCX1BMSUVSX2Jhc2VsaW5lLnBkZiIpCmdncGxvdDI6Omdnc2F2ZShwbG90LmZpbGUsIHBsb3QgPSBwICsgCiAgICAgICAgICAgICAgICAgIGdncGxvdDI6OnRoZW1lKHRleHQgPSBnZ3Bsb3QyOjplbGVtZW50X3RleHQoc2l6ZSA9IDE1KSkpCmBgYAoKCmBgYHtyfQojIHdoaWNoIGFyZSBsaWtlbHkgdGhlIDkgSUZOLW5lZ2F0aXZlIHBhdGllbnRzIGluIApsb3cuaWZuLnNsZSA8LSBkcGx5cjo6ZmlsdGVyKGJhc2VsaW5lLmRmLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGBEaXNlYXNlIHN0YXRlYCA9PSAiU0xFIikgJT4lCiAgZHBseXI6Omdyb3VwX2J5KExWKSAlPiUKICBkcGx5cjo6dG9wX24oKFZhbHVlICogLTEpLCBuID0gOSkgJT4lCiAgZHBseXI6OmFycmFuZ2UoTFYpCmxvdy5pZm4uc2xlCmBgYAoKYGBge3J9CnRhYmxlKGxvdy5pZm4uc2xlJFBhdGllbnQpCmBgYAoKV2UnbGwgY2FsbCBhbnl0aGluZyB0aGF0J3Mgb25lIG9mIHRoZSA5IGxvd2VzdCBzYW1wbGVzIChpbiBtb3JlIHRoYW4gb25lIExWKSAKSUZOLW5lZ2F0aXZlIGZvciB0aGlzIG1ldGhvZC4KCmBgYHtyfQpsb3cuaWZuLnBhdCA8LSBuYW1lcyh0YWJsZShsb3cuaWZuLnNsZSRQYXRpZW50KSlbdGFibGUobG93Lmlmbi5zbGUkUGF0aWVudCkgPiAxXQpwbGFjZWJvLnBhdCA8LSAKICB1bmlxdWUoYmFzZWxpbmUuZGYkUGF0aWVudFt3aGljaChiYXNlbGluZS5kZiRHcm91cCA9PSAiUGxhY2VibyIpXSkKIyByZW1vdmUgbG93IElGTiBwYXRpZW50cyB0aGF0IGFyZSBvbiBwbGFjZWJvCmxvdy5pZm4ucGF0IDwtIHNldGRpZmYobG93Lmlmbi5wYXQsIHBsYWNlYm8ucGF0KQpoaS5pZm4ucGF0IDwtIHNldGRpZmYoYXJyYXkuYXR0JFBhdGllbnQsIGMobG93Lmlmbi5wYXQsIHBsYWNlYm8ucGF0KSkKaGkuaWZuLnBhdCA8LSBoaS5pZm4ucGF0W2dyZXAoInBhdGllbnQiLCBoaS5pZm4ucGF0KV0KCmlmbi5iLmRmIDwtCiAgaWZuLmIuZGYgJT4lIAogICAgZHBseXI6Om11dGF0ZShgSUZOLWxldmVsYCA9IHJlcChOQSwgbnJvdyhpZm4uYi5kZikpKSAlPiUKICAgIGRwbHlyOjptdXRhdGUoYElGTi1sZXZlbGAgPSBkcGx5cjo6Y2FzZV93aGVuKAogICAgICAoUGF0aWVudCAlaW4lIGxvdy5pZm4ucGF0KSB+ICJJRk4tbmVnYXRpdmUiLCAKICAgICAgKFBhdGllbnQgJWluJSBwbGFjZWJvLnBhdCkgfiAiUGxhY2VibyIsCiAgICAgIChQYXRpZW50ICVpbiUgaGkuaWZuLnBhdCkgfiAiSUZOLXBvc2l0aXZlIgogICAgKSkgJT4lCiAgICByZWFkcjo6d3JpdGVfdHN2KHBhdGggPSBmaWxlLnBhdGgocmVzdWx0cy5kaXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRS1HRU9ELTM5MDg4X1NMRS1XQl9QTElFUl9JRk5fQi50c3YiKSkKYGBgCgpgYGB7cn0Kcm0obGlzdCA9IHNldGRpZmYobHMoKSwgYygiJT4lIiwgIm1vZC5zdW1tYXJ5LmRmIiwgImFycmF5LmF0dCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgInNsZS5iLmRmIiwgInBsb3QuZGlyIiwgInJlc3VsdHMuZGlyIikpKQpgYGAKCiMjIyBSZWFkIGluIFNMRSBCIG1hdHJpeCAoaW4gcmVjb3VudDIgc3BhY2UpCgpgYGB7cn0KIyByZWFkIGluIEIgbWF0cml4CnJlYy5iLmZpbGUgPC0gZmlsZS5wYXRoKCJyZXN1bHRzIiwgIjA3IiwgIlNMRS1XQl9CX21hdHJpeF9yZWNvdW50Ml9tb2RlbC5SRFMiKQpyZWNvdW50LmIubWF0IDwtIGFzLmRhdGEuZnJhbWUocmVhZFJEUyhyZWMuYi5maWxlKSkKcmVjb3VudC5iLm1hdCA8LSB0aWJibGU6OnJvd25hbWVzX3RvX2NvbHVtbihyZWNvdW50LmIubWF0LCB2YXIgPSAiQW5ub3RhdGVkIikKCiMgcmVzaGFwZQpyZWNvdW50LmIuZGYgPC0gcmVzaGFwZTI6Om1lbHQocmVjb3VudC5iLm1hdCkKcmVjb3VudC5iLmRmIDwtIAogIHJlY291bnQuYi5kZiAlPiUKICAgIGRwbHlyOjptdXRhdGUoTFYgPSByZXAocGFzdGUwKCJMViIsIDE6bnJvdyhyZWNvdW50LmIubWF0KSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG5jb2wocmVjb3VudC5iLm1hdCkgLSAxKSkKY29sbmFtZXMocmVjb3VudC5iLmRmKSA8LSBjKCJBbm5vdGF0ZWQiLCAiU2FtcGxlIiwgIlZhbHVlIiwgIkxWIikKcmVjb3VudC5iLmRmIDwtIHJlY291bnQuYi5kZlssIGMoIlNhbXBsZSIsICJMViIsICJBbm5vdGF0ZWQiLCAiVmFsdWUiKV0KaGVhZChyZWNvdW50LmIuZGYpCmBgYAoKYGBge3J9Cmlmbi5iLmRmIDwtIHJlY291bnQuYi5kZiAlPiUKICAgICAgICAgICAgICBkcGx5cjo6ZmlsdGVyKExWICVpbiUgYygiTFYxMTYiLCAiTFYxNDAiKSkgJT4lCiAgICAgICAgICAgICAgZHBseXI6OnJpZ2h0X2pvaW4oeSA9IGFycmF5LmF0dCwgYnkgPSBjKCJTYW1wbGUiID0gIlNvdXJjZSBOYW1lIikpCgpiYXNlbGluZS5kZiA8LSAKICBkcGx5cjo6YmluZF9yb3dzKGRwbHlyOjpmaWx0ZXIoaWZuLmIuZGYsIERheSA9PSAiYmFzZWxpbmUiKSwKICAgICAgICAgICAgICAgICAgIGRwbHlyOjpmaWx0ZXIoaWZuLmIuZGYsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgRGlzZWFzZSBzdGF0ZWAgPT0gImhlYWx0aHkiICYgIShncmVwbCgidW5zdGltdWxhdGVkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVHJlYXRtZW50KSkpKQpgYGAKCmBgYHtyfQpwIDwtIGJhc2VsaW5lLmRmICU+JQogIGdncGxvdDI6OmdncGxvdChnZ3Bsb3QyOjphZXMoeCA9IGBEaXNlYXNlIHN0YXRlYCwgeSA9IFZhbHVlKSkgKwogIGdncGxvdDI6Omdlb21faml0dGVyKGdncGxvdDI6OmFlcyhjb2xvdXIgPSBgRGlzZWFzZSBzdGF0ZWApLCB3aWR0aCA9IDAuMikgKyAKICBnZ3Bsb3QyOjpzdGF0X3N1bW1hcnkoZnVuLnkgPSAibWVkaWFuIiwgc2l6ZSA9IDQsIHNoYXBlID0gMTgsCiAgICAgICAgICAgICAgICAgICAgICAgIGdlb20gPSAicG9pbnQiLCBjb2xvciA9ICJibGFjayIpICsKICBnZ3Bsb3QyOjpmYWNldF9ncmlkKH4gTFYpICsKICBnZ3Bsb3QyOjp0aGVtZV9idygpICsgCiAgZ2dwbG90Mjo6bGFicyh5ID0gIkxWIHZhbHVlIiwKICAgICAgICAgICAgICAgIHRpdGxlID0gInJlY291bnQgUExJRVIgLSBCYXNlbGluZSIsCiAgICAgICAgICAgICAgICBzdWJ0aXRsZSA9ICJMYXV3ZXJ5cywgZXQgYWwuIikgKwogIGdncGxvdDI6OnNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJzZWFncmVlbjMiLCAiIzMxODJiZCIpKSArCiAgZ2dwbG90Mjo6dGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpwCmBgYAoKSXQgbG9va3MgbGlrZSB0aGVyZSdzIGxpdHRsZSBkaWZmZXJlbmNlIGJldHdlZW4gYExWMTQwYCBpbiBoZWFsdGh5IGNvbnRyb2xzIGFuZCAKcGF0aWVudHMgd2l0aCBTTEUgKGF0IGJhc2VsaW5lKS4gClRoaXMgTFYgc2hvdWxkIGNhcHR1cmUgSUZOLWdhbW1hIHNpZ25hbGluZyAobXVjaCBsaWtlIGBNMy40YCBhbmQgYE01LjEyYCkgYW5kIAppdCBzaG93cyBhIHNpbWlsYXIgcGF0dGVybiBvZiBleHByZXNzaW9uIHRvIGBNNS4xMmAuCgpgYGB7cn0KIyBzYXZlIHBsb3QKcGxvdC5maWxlIDwtIGZpbGUucGF0aChwbG90LmRpciwKICAgICAgICAgICAgICAgICAgICAgICAiRS1HRU9ELTM5MDg4X3JlY291bnQyX1BMSUVSX2Jhc2VsaW5lLnBkZiIpCmdncGxvdDI6Omdnc2F2ZShwbG90LmZpbGUsIHBsb3QgPSBwICsgCiAgICAgICAgICAgICAgICAgIGdncGxvdDI6OnRoZW1lKHRleHQgPSBnZ3Bsb3QyOjplbGVtZW50X3RleHQoc2l6ZSA9IDE1KSkpCmBgYAoKV2UnbGwgY2FsbCB0aGUgOSBwYXRpZW50cyB3aXRoIHRoZSBsb3dlc3QgYExWMTE2YCB2YWx1ZXMgYXQgYmFzZWxpbmUgCklGTi1uZWdhdGl2ZSBpbiB0aGlzIG1ldGhvZC4KCmBgYHtyfQojIHdoaWNoIGFyZSBsaWtlbHkgdGhlIDkgSUZOLW5lZ2F0aXZlIHBhdGllbnRzIGluIApsb3cuaWZuLnNsZSA8LSBkcGx5cjo6ZmlsdGVyKGJhc2VsaW5lLmRmLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGBEaXNlYXNlIHN0YXRlYCA9PSAiU0xFIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBMViA9PSAiTFYxMTYiKSAlPiUKICBkcGx5cjo6Z3JvdXBfYnkoTFYpICU+JQogIGRwbHlyOjp0b3BfbigoVmFsdWUgKiAtMSksIG4gPSA5KQoKbG93Lmlmbi5wYXQgPC0gbG93Lmlmbi5zbGUkUGF0aWVudApwbGFjZWJvLnBhdCA8LSAKICB1bmlxdWUoYmFzZWxpbmUuZGYkUGF0aWVudFt3aGljaChiYXNlbGluZS5kZiRHcm91cCA9PSAiUGxhY2VibyIpXSkKIyByZW1vdmUgbG93IElGTiBwYXRpZW50cyB0aGF0IGFyZSBvbiBwbGFjZWJvCmxvdy5pZm4ucGF0IDwtIHNldGRpZmYobG93Lmlmbi5wYXQsIHBsYWNlYm8ucGF0KQpoaS5pZm4ucGF0IDwtIHNldGRpZmYoYXJyYXkuYXR0JFBhdGllbnQsIGMobG93Lmlmbi5wYXQsIHBsYWNlYm8ucGF0KSkKaGkuaWZuLnBhdCA8LSBoaS5pZm4ucGF0W2dyZXAoInBhdGllbnQiLCBoaS5pZm4ucGF0KV0KYGBgCgpgYGB7cn0KaWZuLmIuZGYgPC0KICBpZm4uYi5kZiAlPiUgCiAgICBkcGx5cjo6bXV0YXRlKGBJRk4tbGV2ZWxgID0gcmVwKE5BLCBucm93KGlmbi5iLmRmKSkpICU+JQogICAgZHBseXI6Om11dGF0ZShgSUZOLWxldmVsYCA9IGRwbHlyOjpjYXNlX3doZW4oCiAgICAgIChQYXRpZW50ICVpbiUgbG93Lmlmbi5wYXQpIH4gIklGTi1uZWdhdGl2ZSIsIAogICAgICAoUGF0aWVudCAlaW4lIHBsYWNlYm8ucGF0KSB+ICJQbGFjZWJvIiwKICAgICAgKFBhdGllbnQgJWluJSBoaS5pZm4ucGF0KSB+ICJJRk4tcG9zaXRpdmUiCiAgICApKSAlPiUKICByZWFkcjo6d3JpdGVfdHN2KHBhdGggPSBmaWxlLnBhdGgocmVzdWx0cy5kaXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJFLUdFT0QtMzkwODhfcmVjb3VudDJfUExJRVJfSUZOX0IudHN2IikpCmBgYAoKYGBge3J9CnJtKGxpc3QgPSBzZXRkaWZmKGxzKCksIGMoIiU+JSIsICJtb2Quc3VtbWFyeS5kZiIsICJyZWNvdW50LmIuZGYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICJzbGUuYi5kZiIsICJwbG90LmRpciIsICJyZXN1bHRzLmRpciIpKSkKYGBgCgojIyBBTUcgODExCgoqKkUtR0VPRC03ODE5MzsgV2VsY2hlciwgZXQgYWwuIDIwMTUuKioKCkknbGwgc3VtbWFyaXplIHNvbWUgb2YgdGhlIHJlbGV2YW50IHBvaW50cyBmcm9tIFdlbGNoZXIsIGV0IGFsLjoKCiogQU1HIDgxMSBpcyBhIG1vbm9jbG9uYWwgYW50aWJvZHkgYWdhaW5zdCBJRk4tZ2FtbWEgKHR5cGUgSUkgSUZOKS4KKiBQYXRpZW50cyB3aXRoIFNMRSByZWNlaXZlZCBlaXRoZXIgQU1HIDgxMSAoZGlmZmVyZW50IGRvc2VzIGFuZC9vciAKYWRtaW5zdHJhdGlvbiBtZXRob2RzKSBvciBwbGFjZWJvLgoqIFdob2xlIGJsb29kIHdhcyBjb2xsZWN0ZWQgZnJvbSBwYXRpZW50cyB3aXRoIFNMRSBhdCBkYXkgMSAoYmFzZWxpbmUpLCBkYXkgMTUsIApkYXkgNTYsIGFuZCBhdCB0aGUgZW5kIG9mIHRoZSBzdHVkeSAoRU9TKS4KKiBXaG9sZSBibG9vZCB3YXMgYWxzbyBjb2xsZWN0ZWQgZnJvbSBoZWFsdGh5IGNvbnRyb2xzIGFuZCBlaXRoZXIgc3RpbXVsYXRlZCAKd2l0aCBJRk4tZ2FtbWEgZm9yICgwLCAyNCwgb3IgNDggaHJzKSBvciB3ZXJlIHVudHJlYXRlZC4KVGhlIGF1dGhvcnMgaWRlbnRpZmllZCBnZW5lcyB0aGF0IHdlcmUgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGJldHdlZW4gCnVuc3RpbXVsYXRlZCBhbmQgSUZOLWdhbW1hIHN0aW11bGF0ZWQgc2FtcGxlcyAoSUZOLWdhbW1hIHNpZ25hdHVyZSkuCklGTi1nYW1tYSBzY29yZXMgY2FsY3VsYXRlZCB1c2luZyB0aGVzZSBnZW5lcyB3ZXJlIHNpbWlsYXIgdG8gb3RoZXIgCihwcmV2aW91c2x5IHB1Ymxpc2hlZCkgSUZOIGdlbmUgc2V0cy4KKiBQb3N0LXRyZWF0bWVudCAoQU1HODExKSBzYW1wbGVzIHNob3dlZCBhIHNpZ25pZmljYW50IGRlY3JlYXNlIGluIGEgCm51bWJlciBvZiB0aGUgSUZOLWdhbW1hIHNpZ25hdHVyZSBnZW5lcyAKCiMjIyMgU2FtcGxlLWRhdGEgcmVsYXRpb25zaGlwIGZpbGUKCmBgYHtyfQplLjc4MTkzLnNkcmYgPC0gZGF0YS50YWJsZTo6ZnJlYWQoZmlsZS5wYXRoKCJkYXRhIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNhbXBsZV9pbmZvIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRS1HRU9ELTc4MTkzLnNkcmYudHh0IiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhLnRhYmxlID0gRkFMU0UpCgphcnJheS5hdHQgPC0gZS43ODE5My5zZHJmWywgYygiU291cmNlIE5hbWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ29tbWVudCBbU2FtcGxlX3NvdXJjZV9uYW1lXSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDaGFyYWN0ZXJpc3RpY3MgW3N1YmplY3RdIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNoYXJhY3RlcmlzdGljcyBbdHJlYXRtZW50IGRheV0iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ29tbWVudCBbU2FtcGxlX3RpdGxlXSIpXQoKYXJyYXkuYXR0IDwtIAogIGFycmF5LmF0dCAlPiUKICAgIGRwbHlyOjptdXRhdGUoU2FtcGxlID0gc3ViKCIgMSIsICIiLCBgU291cmNlIE5hbWVgKSkgJT4lCiAgICBkcGx5cjo6c2VsZWN0KC1gU291cmNlIE5hbWVgKQoKY29sbmFtZXMoYXJyYXkuYXR0KVsxOjRdIDwtIGMoIkRpc2Vhc2Ugc3RhdGUiLCAiU3ViamVjdCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRGF5IiwgIlRyZWF0bWVudCIpCgoKIyByZW1vdmUgSUZOLWdhbW1hIHN0aW11bGF0ZWQgc2FtcGxlcywgd2Ugd29uJ3QgYmUgdXNpbmcgdGhlbSBkb3duc3RyZWFtCmFycmF5LmF0dCA8LQogIGFycmF5LmF0dCAlPiUKICBkcGx5cjo6ZmlsdGVyKCEoZ3JlcGwoIisgSUZOLWciLCBUcmVhdG1lbnQpKSkgJT4lCiAgZHBseXI6OnNlbGVjdCgtVHJlYXRtZW50KSAgJT4lCiAgZHBseXI6Om11dGF0ZShgRGlzZWFzZSBzdGF0ZWAgPQogICAgICAgICAgICAgICAgICBkcGx5cjo6cmVjb2RlKGBEaXNlYXNlIHN0YXRlYCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInN5c3RlbWljIGx1cHVzIGVyeXRoZW1hdG9zdXMgKFNMRSkgcGF0aWVudCIgPSAiU0xFIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiaGVhbHRoeSB2b2x1bnRlZXIiID0gImhlYWx0aHkiKSkKYGBgCgojIyMgTW9kdWxhciB0cmFuc2NyaXB0aW9uYWwgYW5hbHlzZXMKCmBgYHtyfQojIGpvaW4gc2FtcGxlIGluZm9ybWF0aW9uIHdpdGggdGhlIHN1bW1hcnkgb2YgdGhlIG1vZHVsZSBnZW5lIHNldHMKbW9kLm1ldGEuZGYgPC0gZHBseXI6OnJpZ2h0X2pvaW4obW9kLnN1bW1hcnkuZGYsIGFycmF5LmF0dCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gYygiU291cmNlIE5hbWUiID0gIlNhbXBsZSIpKQojIHdyaXRlIHRvIGZpbGUKbW9kLmZpbGUgPC0gZmlsZS5wYXRoKHJlc3VsdHMuZGlyLAogICAgICAgICAgICAgICAgICAgICAgIkUtR0VPRC03ODE5M19DaGljaGVfZXRfYWxfbW9kdWxlLnRzdiIpCnJlYWRyOjp3cml0ZV90c3YobW9kLm1ldGEuZGYsIHBhdGggPSBtb2QuZmlsZSkKYGBgCgpgYGB7cn0Kcm0obW9kLmZpbGUsIG1vZC5tZXRhLmRmKQpgYGAKCiMjIyBQTElFUiB0cmFpbmVkIG9uIFNMRSBXQiBjb21wZW5kaXVtCgpgYGB7cn0KIyBqb2luIHNhbXBsZSBpbmZvIHdpdGggU0xFIFdCIFBMSUVSIExWcwpzbGUuYi5tZXRhLmRmIDwtIGRwbHlyOjpyaWdodF9qb2luKHNsZS5iLmRmLCBhcnJheS5hdHQsIGJ5ID0gIlNhbXBsZSIpICU+JQogIGRwbHlyOjpmaWx0ZXIoTFYgJWluJSBjKCJMVjYiLCAiTFY2OSIsICJMVjExMCIpKQoKIyB3cml0ZSB0byBmaWxlCnNsZS5iLmZpbGUgPC0gZmlsZS5wYXRoKHJlc3VsdHMuZGlyLCAiRS1HRU9ELTc4MTkzX1NMRS1XQl9QTElFUl9JRk5fQi50c3YiKQpyZWFkcjo6d3JpdGVfdHN2KHNsZS5iLm1ldGEuZGYsIHBhdGggPSBzbGUuYi5maWxlKQpgYGAKCmBgYHtyfQpybShzbGUuYi5tZXRhLmRmKQpgYGAKCiMjIyBQTElFUiB0cmFpbmVkIG9uIHJlY291bnQyCgpgYGB7cn0KIyBqb2luIHNhbXBsZSBpbmZvIHdpdGggU0xFIFdCIFBMSUVSIExWcwpyZWMuYi5tZXRhLmRmIDwtIGRwbHlyOjpyaWdodF9qb2luKHJlY291bnQuYi5kZiwgYXJyYXkuYXR0LCBieSA9ICJTYW1wbGUiKSAlPiUKICBkcGx5cjo6ZmlsdGVyKExWICVpbiUgYygiTFYxMTYiLCAiTFYxNDAiKSkKCiMgd3JpdGUgdG8gZmlsZQpyZWMuYi5maWxlIDwtIGZpbGUucGF0aChyZXN1bHRzLmRpciwgIkUtR0VPRC03ODE5M19yZWNvdW50Ml9QTElFUl9JRk5fQi50c3YiKQpyZWFkcjo6d3JpdGVfdHN2KHJlYy5iLm1ldGEuZGYsIHBhdGggPSByZWMuYi5maWxlKQpgYGAKCg==