J. Taroni 2018

In this notebook, we’ll examine how IFN-inducible or IFN-associated gene signatures change during treatment with targeted therapies that are designed to block the action of some IFNs.

Briefly, we’ll look at two therapies in the context of SLE: IFN-K (which blocks IFN-alpha, type I IFN; Lauwerys, et al. Arthritis Rheum. 2013.) and AMG 811 (which blocks IFN-gamma, type II IFN; Welcher, et al. Arthritis Rheumatol. 2015.).

For a little more background, including summaries of the main findings in the original papers, see the 7-sle_ifn_data_prep notebook.

Directory setup

`%>%` <- 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
# plot and result directory setup for this notebook
plot.dir <- file.path("plots", "10")
dir.create(plot.dir, recursive = TRUE, showWarnings = FALSE)
results.dir <- file.path("results", "10")
dir.create(results.dir, recursive = TRUE, showWarnings = FALSE)
# directory that was used for data prep
data.prep.dir <- file.path("results", "09")
# set seed for reproducibility (plot jitter)
set.seed(123)

IFN-K treatment

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

Functions

In the IFN-K trial specifically, we’ll examine how IFN expression has changed from baseline. This is similar to the analyses performed in the original paper. We need a custom function for this and for plotting, as we’ll be repeating this with three different methods: modular framework, SLE WB PLIER, recount2 PLIER.

CalculateChangeFromBaseline <- function(df, variable.id, value.id) {
  # Given a data.frame that contains values (value.id) from the longitudinal 
  # study E-GEOD-39088, this function will calculate the change in expression
  # level from baseline. Not intended for use outside this context (& env)!
  #
  # Args:
  #   df: data.frame that contains day, some variable (i.e., module or latent
  #       variable id), and the value for that variable (i.e., mean expression);
  #       must contain Day and Patient columns
  #   variable.id: the name of the column that contains the module or latent
  #               variable identifier 
  #   value.id: the name of the column that contains the values to be subtracted
  #             from one another (e.g., contains mean expression for a sample
  #             for above variable)
  # 
  # Returns:
  #  df with the calculated changes appended in the "Change" column
  
  col.check <- all(("Day" %in% colnames(df)), ("Patient" %in% colnames(df)))
  if (!col.check) {
    stop("This function expects the input data.frame to contain colnames 
         'Day' and 'Patient'")
  }
  
  input.col.check <- all((variable.id %in% colnames(df)), 
                          (value.id %in% colnames(df)))
  if (!input.col.check) {
    stop("One or more of 'variable.id' and 'value.id' are not in
         colnames(df)")
  }
  
  if (!("baseline" %in% df$Day)) {
    stop("baseline should be a time point in Day column")
  }
  
  change.summary <- rep(NA, nrow(df))
  # for each patient, how has the value changed?
  for (pat in unique(df$Patient)) {
    # for each variable (e.g., module)
    for(var.iter in unique(df[, variable.id])) {
      # identify the baseline value index
      baseline.indx <- which(df[, variable.id] == var.iter & 
                               df$Patient == pat &
                               df$Day == "baseline")
      # for all days, including baseline (baseline should equal zero)
      for(day in unique(df$Day)) {
        day.indx <- which(df[, variable.id] == var.iter & 
                            df$Patient == pat &
                            df$Day == day)
        # subtract the baseline value from the day value
        change.summary[day.indx] <- 
          df[, value.id][day.indx] - df[, value.id][baseline.indx]
      }
    }
  }
  
  # add this column to the data.frame
  df$Change <- change.summary 
  return(df)
}
PlotChangeFromBaseline <- function(df, y.label, plot.title, plot.path,
                                   facets = "Module ~ Day",
                                   plot.subtitle = "Lauwerys, et al.") {
  # Given a data.frame that with calculated changes from baseline 
  # (from CalculateChangeFromBaseline), make boxplots comparing the three groups
  # of patients -- placebo, IFN-negative, IFN-positive -- for each variable
  # (e.g. module) ~ each day of the study (e.g., day 112, day 168)
  # Not intended for use outside this context (& env)!
  #
  # Args:
  #   df: data.frame output from CalculateChangeFromBaseline
  #   y.label: y-axis label (string)
  #   plot.title: plot title (string)
  #   plot.path: full path for plot file (string)
  #   facets: formula passed to ggplot2::facet_wrap(), default is "Module ~ Day"
  #   plot.subtitle: plot subtitle (string)
  #
  # Returns:
  #   NULL; plot saved at plot.path
  
  # reorder IFN-level for display
  df$`IFN-level` <- 
   factor(df$`IFN-level`,
          levels = c("Placebo", "IFN-negative", "IFN-positive"))
  # plot
  p <- ggplot2::ggplot(dplyr::filter(df, Day != "baseline"), 
                       ggplot2::aes(x = `IFN-level`, 
                                    y = `Change`, color = `IFN-level`)) +
    ggplot2::geom_boxplot(notch = TRUE) + 
    ggplot2::theme_bw() + 
    ggplot2::geom_jitter(alpha = 0.5, width = 0.2) +
    ggplot2::facet_wrap(as.formula(facets), ncol = 2) +
    ggplot2::theme(axis.text.x = ggplot2::element_text(angle = 45, hjust = 1),
                   legend.position = "none", 
                   text = ggplot2::element_text(size = 15)) +
    ggplot2::labs(y = y.label,
                  title = plot.title,
                  subtitle = plot.subtitle) +
    ggplot2::scale_color_manual(values = c("#969696", "#8073ac", "#e08214"))
  
  ggplot2::ggsave(plot.path, plot = p, width = 8.5, height = 14, units = "in")
}

Modular transcriptional analyses

# read in tidy data
mod.file <- file.path(data.prep.dir, "E-GEOD-39088_Chiche_et_al_module.tsv")
mod.summary.df <- readr::read_tsv(mod.file)
Parsed with column specification:
cols(
  `Source Name` = col_character(),
  Summary = col_double(),
  Module = col_character(),
  Day = col_character(),
  `Disease state` = col_character(),
  Treatment = col_character(),
  Patient = col_character(),
  Group = col_character(),
  `IFN-level` = col_character()
)
# only keep SLE patients
ifn.summary.df <-   
  mod.summary.df %>% 
    dplyr::filter(grepl("SLE patient", Patient))
ifn.summary.df <- 
  CalculateChangeFromBaseline(df = as.data.frame(ifn.summary.df),
                              variable.id = "Module",
                              value.id = "Summary")
df.file <- file.path(results.dir, "E-GEOD-39088_IFNk_Chiche_modules_change.tsv")
readr::write_tsv(ifn.summary.df, df.file)

Plot

plot.file <- file.path(plot.dir, "E-GEOD-39088_IFNk_Chiche_modules_change.pdf")
PlotChangeFromBaseline(df = ifn.summary.df,
                       y.label = "Change in Expression Summary",
                       plot.title = "IFN Modular Framework Expression - 
                          IFN-K treatment",
                       plot.path = plot.file)
rm(list = setdiff(ls(), c("%>%", "CalculateChangeFromBaseline",
                          "PlotChangeFromBaseline",
                          "plot.dir", "results.dir", "data.prep.dir")))

PLIER trained on SLE WB compendium

sle.b.file <- file.path(data.prep.dir, "E-GEOD-39088_SLE-WB_PLIER_IFN_B.tsv")
sle.b.df <- readr::read_tsv(sle.b.file)
Parsed with column specification:
cols(
  Sample = col_character(),
  LV = col_character(),
  Annotated = col_character(),
  Value = col_double(),
  Day = col_character(),
  `Disease state` = col_character(),
  Treatment = col_character(),
  Patient = col_character(),
  Group = col_character(),
  `IFN-level` = col_character()
)
# SLE patients only
ifn.b.df <- sle.b.df %>%
  dplyr::filter(grepl("SLE patient", Patient))
ifn.b.df <- CalculateChangeFromBaseline(df = as.data.frame(ifn.b.df),
                                        variable.id = "LV",
                                        value.id = "Value")
df.file <- file.path(results.dir, "E-GEOD-39088_IFNk_SLE_PLIER_change.tsv")
readr::write_tsv(ifn.b.df, df.file)
plot.file <- file.path(plot.dir, "E-GEOD-39088_IFNk_SLE_PLIER_change.pdf")
PlotChangeFromBaseline(df = ifn.b.df,
                       y.label = "Change in LV value",
                       plot.title = "SLE WB PLIER - IFN-K treatment",
                       plot.path = plot.file,
                       facets = "LV ~ Day")
rm(list = setdiff(ls(), c("%>%", "CalculateChangeFromBaseline",
                          "PlotChangeFromBaseline",
                          "plot.dir", "results.dir", "data.prep.dir")))

PLIER trained on recount2

recount.b.file <- file.path(data.prep.dir, 
                            "E-GEOD-39088_recount2_PLIER_IFN_B.tsv")
recount.b.df <- readr::read_tsv(recount.b.file)
Parsed with column specification:
cols(
  Sample = col_character(),
  LV = col_character(),
  Annotated = col_character(),
  Value = col_double(),
  Day = col_character(),
  `Disease state` = col_character(),
  Treatment = col_character(),
  Patient = col_character(),
  Group = col_character(),
  `IFN-level` = col_character()
)
# SLE patients only
ifn.b.df <- recount.b.df %>%
  dplyr::filter(grepl("SLE patient", Patient))
ifn.b.df <- CalculateChangeFromBaseline(df = as.data.frame(ifn.b.df),
                                        variable.id = "LV",
                                        value.id = "Value")
df.file <- file.path(results.dir, "E-GEOD-39088_IFNk_recount2_PLIER_change.tsv")
readr::write_tsv(ifn.b.df, df.file)
# plot
plot.file <- file.path(plot.dir, "E-GEOD-39088_IFNk_recount2_PLIER_change.pdf")
PlotChangeFromBaseline(df = ifn.b.df,
                       y.label = "Change in LV value",
                       plot.title = "recount2 PLIER - IFN-K treatment",
                       plot.path = plot.file,
                       facets = "LV ~ Day")
rm(list = setdiff(ls(), c("%>%", "results.dir", "plot.dir", 
                          "data.prep.dir")))

AMG 811

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

Plotting Function

We’ll want to generate a boxplot the interaction between disease state (e.g., healthy, SLE) and time point (day of trial) for each of the three methods. We’ll write a custom plotting function for this.

PlotInteraction <- function(df, y.var, wrap.var, y.label, plot.title,
                            plot.subtitle = "Welcher, et al.") {
  # Given a data.frame that contains a "summary" expression level of some kind
  # for E-GEOD-78193 samples, make a boxplot where x/groups are 
  # interaction(Disease state, Day).  Not intended for use outside this 
  # context (& env)!
  #
  # Args:
  #   df: a (long form) data.frame containing the measurements for E-GEOD-78193
  #   y.var: variable used as y.var (string; evaluated with ggplot2::aes_string)
  #   wrap.var: string passed to ggplot2::facet_wrap; used for multiple LVs 
  #             or modules
  #   y.label: string, label for y-axis 
  #   plot.title: string, plot title
  #   plot.subtitle: string, plot subtitle; default "Welcher, et al."
  #
  # Returns:
  #   ggplot2::ggplot object
  
  ggplot2::ggplot(df, 
                  ggplot2::aes(x = interaction(`Disease state`, 
                                           `Day`), 
                               fill = interaction(`Disease state`, 
                                                  `Day`))) + 
    ggplot2::geom_boxplot(ggplot2::aes_string(y = y.var)) + 
    ggplot2::geom_point(ggplot2::aes_string(y = y.var),
                        alpha = 0.3, position = "jitter") +
    ggplot2::facet_wrap(as.formula(paste("~", wrap.var))) +
    ggplot2::theme_bw() + 
    ggplot2::theme(axis.text.x = ggplot2::element_text(angle = 45, hjust = 1),
                   legend.position = "none",
                   text = ggplot2::element_text(size = 15)) +
    ggplot2::labs(x = "interaction(Disease State, Day)",
                  y = y.label,
                  title = plot.title,
                  subtitle = plot.subtitle) +
    ggplot2::scale_fill_manual(values = c("seagreen3", "#deebf7", "#9ecae1",
                                          "#3182bd", "white")) +
    ggplot2::scale_x_discrete(labels = c("healthy", "SLE baseline", 
                                         "SLE day 15", "SLE day 56", 
                                         "SLE EOS"))
}

Modular transcriptional analyses

mod.file <- file.path(data.prep.dir,
                      "E-GEOD-78193_Chiche_et_al_module.tsv")
mod.summary.df <- readr::read_tsv(mod.file)
Parsed with column specification:
cols(
  `Source Name` = col_character(),
  Summary = col_double(),
  Module = col_character(),
  `Disease state` = col_character(),
  Subject = col_character(),
  Day = col_character()
)
p <- PlotInteraction(df = mod.summary.df,
                     y.var = "Summary",
                     wrap.var = "Module",
                     y.label = "Mean expression of genes in module (per sample)",
                     plot.title = "IFN Modular Framework Expression - Treatment with AMG 811")
plot.file <- file.path(plot.dir, "E-GEOD-78193_Chiche_et_al_boxplot.pdf")
ggplot2::ggsave(plot.file, plot = p, width = 11, height = 7, units = "in")
rm(list = setdiff(ls(), c("%>%", "results.dir", "plot.dir", 
                          "data.prep.dir", "PlotInteraction")))

PLIER trained on SLE WB compendium

sle.b.file <- file.path(data.prep.dir, "E-GEOD-78193_SLE-WB_PLIER_IFN_B.tsv")
sle.b.df <- readr::read_tsv(sle.b.file)
Parsed with column specification:
cols(
  Sample = col_character(),
  LV = col_character(),
  Annotated = col_character(),
  Value = col_double(),
  `Disease state` = col_character(),
  Subject = col_character(),
  Day = col_character()
)
p <- PlotInteraction(df = sle.b.df,
                     y.var = "Value",
                     wrap.var = "LV",
                     y.label = "LV value",
                     plot.title = "SLE WB PLIER - Treatment with AMG 811")
plot.file <- file.path(plot.dir, "E-GEOD-78193_SLE_PLIER_boxplot.pdf")
ggplot2::ggsave(plot.file, plot = p, width = 11, height = 7, units = "in")
rm(list = setdiff(ls(), c("%>%", "results.dir", "plot.dir", 
                          "data.prep.dir", "PlotInteraction")))

PLIER trained on recount2

recount.b.file <- file.path(data.prep.dir, 
                            "E-GEOD-78193_recount2_PLIER_IFN_B.tsv")
recount.b.df <- readr::read_tsv(recount.b.file)
Parsed with column specification:
cols(
  Sample = col_character(),
  LV = col_character(),
  Annotated = col_character(),
  Value = col_double(),
  `Disease state` = col_character(),
  Subject = col_character(),
  Day = col_character()
)
p <- PlotInteraction(df = recount.b.df,
                     y.var = "Value",
                     wrap.var = "LV",
                     y.label = "LV value",
                     plot.title = "recount2 PLIER - Treatment with AMG 811")
plot.file <- file.path(plot.dir, "E-GEOD-78193_recount2_PLIER_boxplot.pdf")
ggplot2::ggsave(plot.file, plot = p, width = 11, height = 7, units = "in")
LS0tCnRpdGxlOiAiU0xFIElGTiBtb2R1bGF0b3J5IHRoZXJhcGllczogQW5hbHlzaXMiCm91dHB1dDogICAKICBodG1sX25vdGVib29rOiAKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCi0tLQoKKipKLiBUYXJvbmkgMjAxOCoqCgpJbiB0aGlzIG5vdGVib29rLCB3ZSdsbCBleGFtaW5lIGhvdyBJRk4taW5kdWNpYmxlIG9yIElGTi1hc3NvY2lhdGVkIGdlbmUgCnNpZ25hdHVyZXMgY2hhbmdlIGR1cmluZyB0cmVhdG1lbnQgd2l0aCB0YXJnZXRlZCB0aGVyYXBpZXMgdGhhdCBhcmUgZGVzaWduZWQgdG8gCmJsb2NrIHRoZSBhY3Rpb24gb2Ygc29tZSBJRk5zLgoKQnJpZWZseSwgd2UnbGwgbG9vayBhdCB0d28gdGhlcmFwaWVzIGluIHRoZSBjb250ZXh0IG9mIFNMRTogSUZOLUsgKHdoaWNoIGJsb2NrcyAKSUZOLWFscGhhLCB0eXBlIEkgSUZOOyAKW0xhdXdlcnlzLCBldCBhbC4gX0FydGhyaXRpcyBSaGV1bS5fIDIwMTMuXShodHRwczovL2RvaS5vcmcvMTAuMTAwMi9hcnQuMzc3ODUpKSAKYW5kIEFNRyA4MTEgKHdoaWNoIGJsb2NrcyBJRk4tZ2FtbWEsIHR5cGUgSUkgSUZOOyAKW1dlbGNoZXIsIGV0IGFsLiBfQXJ0aHJpdGlzIFJoZXVtYXRvbC5fIDIwMTUuXShodHRwczovL3d3dy5uY2JpLm5sbS5uaWguZ292L3BtYy9hcnRpY2xlcy9QTUM1MDU0OTM1LykpLgoKRm9yIGEgbGl0dGxlIG1vcmUgYmFja2dyb3VuZCwgaW5jbHVkaW5nIHN1bW1hcmllcyBvZiB0aGUgbWFpbiBmaW5kaW5ncyBpbiB0aGUgCm9yaWdpbmFsIHBhcGVycywgc2VlIHRoZSBgNy1zbGVfaWZuX2RhdGFfcHJlcGAgbm90ZWJvb2suCgojIyBEaXJlY3Rvcnkgc2V0dXAKCmBgYHtyfQpgJT4lYCA8LSBkcGx5cjo6YCU+JWAKYGBgCgpgYGB7cn0KIyBwbG90IGFuZCByZXN1bHQgZGlyZWN0b3J5IHNldHVwIGZvciB0aGlzIG5vdGVib29rCnBsb3QuZGlyIDwtIGZpbGUucGF0aCgicGxvdHMiLCAiMTAiKQpkaXIuY3JlYXRlKHBsb3QuZGlyLCByZWN1cnNpdmUgPSBUUlVFLCBzaG93V2FybmluZ3MgPSBGQUxTRSkKcmVzdWx0cy5kaXIgPC0gZmlsZS5wYXRoKCJyZXN1bHRzIiwgIjEwIikKZGlyLmNyZWF0ZShyZXN1bHRzLmRpciwgcmVjdXJzaXZlID0gVFJVRSwgc2hvd1dhcm5pbmdzID0gRkFMU0UpCmBgYAoKYGBge3J9CiMgZGlyZWN0b3J5IHRoYXQgd2FzIHVzZWQgZm9yIGRhdGEgcHJlcApkYXRhLnByZXAuZGlyIDwtIGZpbGUucGF0aCgicmVzdWx0cyIsICIwOSIpCmBgYAoKYGBge3J9CiMgc2V0IHNlZWQgZm9yIHJlcHJvZHVjaWJpbGl0eSAocGxvdCBqaXR0ZXIpCnNldC5zZWVkKDEyMykKYGBgCgojIyBJRk4tSyB0cmVhdG1lbnQKCioqRS1HRU9ELTM5MDg4OyBMYXV3ZXJ5cywgZXQgYWwuIDIwMTMuKioKCiMjIyBGdW5jdGlvbnMKCkluIHRoZSBJRk4tSyB0cmlhbCBzcGVjaWZpY2FsbHksIHdlJ2xsIGV4YW1pbmUgaG93IElGTiBleHByZXNzaW9uIGhhcyAKY2hhbmdlZCBmcm9tIGJhc2VsaW5lLgpUaGlzIGlzIHNpbWlsYXIgdG8gdGhlIGFuYWx5c2VzIHBlcmZvcm1lZCBpbiB0aGUgb3JpZ2luYWwgcGFwZXIuIApXZSBuZWVkIGEgY3VzdG9tIGZ1bmN0aW9uIGZvciB0aGlzIGFuZCBmb3IgcGxvdHRpbmcsIGFzIHdlJ2xsIGJlIHJlcGVhdGluZyB0aGlzCndpdGggdGhyZWUgZGlmZmVyZW50IG1ldGhvZHM6IG1vZHVsYXIgZnJhbWV3b3JrLCBTTEUgV0IgUExJRVIsIHJlY291bnQyIFBMSUVSLgoKYGBge3J9CkNhbGN1bGF0ZUNoYW5nZUZyb21CYXNlbGluZSA8LSBmdW5jdGlvbihkZiwgdmFyaWFibGUuaWQsIHZhbHVlLmlkKSB7CiAgIyBHaXZlbiBhIGRhdGEuZnJhbWUgdGhhdCBjb250YWlucyB2YWx1ZXMgKHZhbHVlLmlkKSBmcm9tIHRoZSBsb25naXR1ZGluYWwgCiAgIyBzdHVkeSBFLUdFT0QtMzkwODgsIHRoaXMgZnVuY3Rpb24gd2lsbCBjYWxjdWxhdGUgdGhlIGNoYW5nZSBpbiBleHByZXNzaW9uCiAgIyBsZXZlbCBmcm9tIGJhc2VsaW5lLiBOb3QgaW50ZW5kZWQgZm9yIHVzZSBvdXRzaWRlIHRoaXMgY29udGV4dCAoJiBlbnYpIQogICMKICAjIEFyZ3M6CiAgIyAgIGRmOiBkYXRhLmZyYW1lIHRoYXQgY29udGFpbnMgZGF5LCBzb21lIHZhcmlhYmxlIChpLmUuLCBtb2R1bGUgb3IgbGF0ZW50CiAgIyAgICAgICB2YXJpYWJsZSBpZCksIGFuZCB0aGUgdmFsdWUgZm9yIHRoYXQgdmFyaWFibGUgKGkuZS4sIG1lYW4gZXhwcmVzc2lvbik7CiAgIyAgICAgICBtdXN0IGNvbnRhaW4gRGF5IGFuZCBQYXRpZW50IGNvbHVtbnMKICAjICAgdmFyaWFibGUuaWQ6IHRoZSBuYW1lIG9mIHRoZSBjb2x1bW4gdGhhdCBjb250YWlucyB0aGUgbW9kdWxlIG9yIGxhdGVudAogICMgICAgICAgICAgICAgICB2YXJpYWJsZSBpZGVudGlmaWVyIAogICMgICB2YWx1ZS5pZDogdGhlIG5hbWUgb2YgdGhlIGNvbHVtbiB0aGF0IGNvbnRhaW5zIHRoZSB2YWx1ZXMgdG8gYmUgc3VidHJhY3RlZAogICMgICAgICAgICAgICAgZnJvbSBvbmUgYW5vdGhlciAoZS5nLiwgY29udGFpbnMgbWVhbiBleHByZXNzaW9uIGZvciBhIHNhbXBsZQogICMgICAgICAgICAgICAgZm9yIGFib3ZlIHZhcmlhYmxlKQogICMgCiAgIyBSZXR1cm5zOgogICMgIGRmIHdpdGggdGhlIGNhbGN1bGF0ZWQgY2hhbmdlcyBhcHBlbmRlZCBpbiB0aGUgIkNoYW5nZSIgY29sdW1uCiAgCiAgY29sLmNoZWNrIDwtIGFsbCgoIkRheSIgJWluJSBjb2xuYW1lcyhkZikpLCAoIlBhdGllbnQiICVpbiUgY29sbmFtZXMoZGYpKSkKICBpZiAoIWNvbC5jaGVjaykgewogICAgc3RvcCgiVGhpcyBmdW5jdGlvbiBleHBlY3RzIHRoZSBpbnB1dCBkYXRhLmZyYW1lIHRvIGNvbnRhaW4gY29sbmFtZXMgCiAgICAgICAgICdEYXknIGFuZCAnUGF0aWVudCciKQogIH0KICAKICBpbnB1dC5jb2wuY2hlY2sgPC0gYWxsKCh2YXJpYWJsZS5pZCAlaW4lIGNvbG5hbWVzKGRmKSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICh2YWx1ZS5pZCAlaW4lIGNvbG5hbWVzKGRmKSkpCiAgaWYgKCFpbnB1dC5jb2wuY2hlY2spIHsKICAgIHN0b3AoIk9uZSBvciBtb3JlIG9mICd2YXJpYWJsZS5pZCcgYW5kICd2YWx1ZS5pZCcgYXJlIG5vdCBpbgogICAgICAgICBjb2xuYW1lcyhkZikiKQogIH0KICAKICBpZiAoISgiYmFzZWxpbmUiICVpbiUgZGYkRGF5KSkgewogICAgc3RvcCgiYmFzZWxpbmUgc2hvdWxkIGJlIGEgdGltZSBwb2ludCBpbiBEYXkgY29sdW1uIikKICB9CiAgCiAgY2hhbmdlLnN1bW1hcnkgPC0gcmVwKE5BLCBucm93KGRmKSkKICAjIGZvciBlYWNoIHBhdGllbnQsIGhvdyBoYXMgdGhlIHZhbHVlIGNoYW5nZWQ/CiAgZm9yIChwYXQgaW4gdW5pcXVlKGRmJFBhdGllbnQpKSB7CiAgICAjIGZvciBlYWNoIHZhcmlhYmxlIChlLmcuLCBtb2R1bGUpCiAgICBmb3IodmFyLml0ZXIgaW4gdW5pcXVlKGRmWywgdmFyaWFibGUuaWRdKSkgewogICAgICAjIGlkZW50aWZ5IHRoZSBiYXNlbGluZSB2YWx1ZSBpbmRleAogICAgICBiYXNlbGluZS5pbmR4IDwtIHdoaWNoKGRmWywgdmFyaWFibGUuaWRdID09IHZhci5pdGVyICYgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZiRQYXRpZW50ID09IHBhdCAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZiREYXkgPT0gImJhc2VsaW5lIikKICAgICAgIyBmb3IgYWxsIGRheXMsIGluY2x1ZGluZyBiYXNlbGluZSAoYmFzZWxpbmUgc2hvdWxkIGVxdWFsIHplcm8pCiAgICAgIGZvcihkYXkgaW4gdW5pcXVlKGRmJERheSkpIHsKICAgICAgICBkYXkuaW5keCA8LSB3aGljaChkZlssIHZhcmlhYmxlLmlkXSA9PSB2YXIuaXRlciAmIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgZGYkUGF0aWVudCA9PSBwYXQgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgZGYkRGF5ID09IGRheSkKICAgICAgICAjIHN1YnRyYWN0IHRoZSBiYXNlbGluZSB2YWx1ZSBmcm9tIHRoZSBkYXkgdmFsdWUKICAgICAgICBjaGFuZ2Uuc3VtbWFyeVtkYXkuaW5keF0gPC0gCiAgICAgICAgICBkZlssIHZhbHVlLmlkXVtkYXkuaW5keF0gLSBkZlssIHZhbHVlLmlkXVtiYXNlbGluZS5pbmR4XQogICAgICB9CiAgICB9CiAgfQogIAogICMgYWRkIHRoaXMgY29sdW1uIHRvIHRoZSBkYXRhLmZyYW1lCiAgZGYkQ2hhbmdlIDwtIGNoYW5nZS5zdW1tYXJ5IAogIHJldHVybihkZikKfQoKUGxvdENoYW5nZUZyb21CYXNlbGluZSA8LSBmdW5jdGlvbihkZiwgeS5sYWJlbCwgcGxvdC50aXRsZSwgcGxvdC5wYXRoLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhY2V0cyA9ICJNb2R1bGUgfiBEYXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBsb3Quc3VidGl0bGUgPSAiTGF1d2VyeXMsIGV0IGFsLiIpIHsKICAjIEdpdmVuIGEgZGF0YS5mcmFtZSB0aGF0IHdpdGggY2FsY3VsYXRlZCBjaGFuZ2VzIGZyb20gYmFzZWxpbmUgCiAgIyAoZnJvbSBDYWxjdWxhdGVDaGFuZ2VGcm9tQmFzZWxpbmUpLCBtYWtlIGJveHBsb3RzIGNvbXBhcmluZyB0aGUgdGhyZWUgZ3JvdXBzCiAgIyBvZiBwYXRpZW50cyAtLSBwbGFjZWJvLCBJRk4tbmVnYXRpdmUsIElGTi1wb3NpdGl2ZSAtLSBmb3IgZWFjaCB2YXJpYWJsZQogICMgKGUuZy4gbW9kdWxlKSB+IGVhY2ggZGF5IG9mIHRoZSBzdHVkeSAoZS5nLiwgZGF5IDExMiwgZGF5IDE2OCkKICAjIE5vdCBpbnRlbmRlZCBmb3IgdXNlIG91dHNpZGUgdGhpcyBjb250ZXh0ICgmIGVudikhCiAgIwogICMgQXJnczoKICAjICAgZGY6IGRhdGEuZnJhbWUgb3V0cHV0IGZyb20gQ2FsY3VsYXRlQ2hhbmdlRnJvbUJhc2VsaW5lCiAgIyAgIHkubGFiZWw6IHktYXhpcyBsYWJlbCAoc3RyaW5nKQogICMgICBwbG90LnRpdGxlOiBwbG90IHRpdGxlIChzdHJpbmcpCiAgIyAgIHBsb3QucGF0aDogZnVsbCBwYXRoIGZvciBwbG90IGZpbGUgKHN0cmluZykKICAjICAgZmFjZXRzOiBmb3JtdWxhIHBhc3NlZCB0byBnZ3Bsb3QyOjpmYWNldF93cmFwKCksIGRlZmF1bHQgaXMgIk1vZHVsZSB+IERheSIKICAjICAgcGxvdC5zdWJ0aXRsZTogcGxvdCBzdWJ0aXRsZSAoc3RyaW5nKQogICMKICAjIFJldHVybnM6CiAgIyAgIE5VTEw7IHBsb3Qgc2F2ZWQgYXQgcGxvdC5wYXRoCiAgCiAgIyByZW9yZGVyIElGTi1sZXZlbCBmb3IgZGlzcGxheQogIGRmJGBJRk4tbGV2ZWxgIDwtIAogICBmYWN0b3IoZGYkYElGTi1sZXZlbGAsCiAgICAgICAgICBsZXZlbHMgPSBjKCJQbGFjZWJvIiwgIklGTi1uZWdhdGl2ZSIsICJJRk4tcG9zaXRpdmUiKSkKCiAgIyBwbG90CiAgcCA8LSBnZ3Bsb3QyOjpnZ3Bsb3QoZHBseXI6OmZpbHRlcihkZiwgRGF5ICE9ICJiYXNlbGluZSIpLCAKICAgICAgICAgICAgICAgICAgICAgICBnZ3Bsb3QyOjphZXMoeCA9IGBJRk4tbGV2ZWxgLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IGBDaGFuZ2VgLCBjb2xvciA9IGBJRk4tbGV2ZWxgKSkgKwogICAgZ2dwbG90Mjo6Z2VvbV9ib3hwbG90KG5vdGNoID0gVFJVRSkgKyAKICAgIGdncGxvdDI6OnRoZW1lX2J3KCkgKyAKICAgIGdncGxvdDI6Omdlb21faml0dGVyKGFscGhhID0gMC41LCB3aWR0aCA9IDAuMikgKwogICAgZ2dwbG90Mjo6ZmFjZXRfd3JhcChhcy5mb3JtdWxhKGZhY2V0cyksIG5jb2wgPSAyKSArCiAgICBnZ3Bsb3QyOjp0aGVtZShheGlzLnRleHQueCA9IGdncGxvdDI6OmVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpLAogICAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLCAKICAgICAgICAgICAgICAgICAgIHRleHQgPSBnZ3Bsb3QyOjplbGVtZW50X3RleHQoc2l6ZSA9IDE1KSkgKwogICAgZ2dwbG90Mjo6bGFicyh5ID0geS5sYWJlbCwKICAgICAgICAgICAgICAgICAgdGl0bGUgPSBwbG90LnRpdGxlLAogICAgICAgICAgICAgICAgICBzdWJ0aXRsZSA9IHBsb3Quc3VidGl0bGUpICsKICAgIGdncGxvdDI6OnNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCIjOTY5Njk2IiwgIiM4MDczYWMiLCAiI2UwODIxNCIpKQogIAogIGdncGxvdDI6Omdnc2F2ZShwbG90LnBhdGgsIHBsb3QgPSBwLCB3aWR0aCA9IDguNSwgaGVpZ2h0ID0gMTQsIHVuaXRzID0gImluIikKfQpgYGAKCiMjIyBNb2R1bGFyIHRyYW5zY3JpcHRpb25hbCBhbmFseXNlcwoKYGBge3J9CiMgcmVhZCBpbiB0aWR5IGRhdGEKbW9kLmZpbGUgPC0gZmlsZS5wYXRoKGRhdGEucHJlcC5kaXIsICJFLUdFT0QtMzkwODhfQ2hpY2hlX2V0X2FsX21vZHVsZS50c3YiKQptb2Quc3VtbWFyeS5kZiA8LSByZWFkcjo6cmVhZF90c3YobW9kLmZpbGUpCmBgYAoKYGBge3J9CiMgb25seSBrZWVwIFNMRSBwYXRpZW50cwppZm4uc3VtbWFyeS5kZiA8LSAgIAogIG1vZC5zdW1tYXJ5LmRmICU+JSAKICAgIGRwbHlyOjpmaWx0ZXIoZ3JlcGwoIlNMRSBwYXRpZW50IiwgUGF0aWVudCkpCgppZm4uc3VtbWFyeS5kZiA8LSAKICBDYWxjdWxhdGVDaGFuZ2VGcm9tQmFzZWxpbmUoZGYgPSBhcy5kYXRhLmZyYW1lKGlmbi5zdW1tYXJ5LmRmKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyaWFibGUuaWQgPSAiTW9kdWxlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWUuaWQgPSAiU3VtbWFyeSIpCmRmLmZpbGUgPC0gZmlsZS5wYXRoKHJlc3VsdHMuZGlyLCAiRS1HRU9ELTM5MDg4X0lGTmtfQ2hpY2hlX21vZHVsZXNfY2hhbmdlLnRzdiIpCnJlYWRyOjp3cml0ZV90c3YoaWZuLnN1bW1hcnkuZGYsIGRmLmZpbGUpCmBgYAoKIyMjIyBQbG90CgpgYGB7cn0KcGxvdC5maWxlIDwtIGZpbGUucGF0aChwbG90LmRpciwgIkUtR0VPRC0zOTA4OF9JRk5rX0NoaWNoZV9tb2R1bGVzX2NoYW5nZS5wZGYiKQpQbG90Q2hhbmdlRnJvbUJhc2VsaW5lKGRmID0gaWZuLnN1bW1hcnkuZGYsCiAgICAgICAgICAgICAgICAgICAgICAgeS5sYWJlbCA9ICJDaGFuZ2UgaW4gRXhwcmVzc2lvbiBTdW1tYXJ5IiwKICAgICAgICAgICAgICAgICAgICAgICBwbG90LnRpdGxlID0gIklGTiBNb2R1bGFyIEZyYW1ld29yayBFeHByZXNzaW9uIC0gCiAgICAgICAgICAgICAgICAgICAgICAgICAgSUZOLUsgdHJlYXRtZW50IiwKICAgICAgICAgICAgICAgICAgICAgICBwbG90LnBhdGggPSBwbG90LmZpbGUpCmBgYAoKYGBge3J9CnJtKGxpc3QgPSBzZXRkaWZmKGxzKCksIGMoIiU+JSIsICJDYWxjdWxhdGVDaGFuZ2VGcm9tQmFzZWxpbmUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICJQbG90Q2hhbmdlRnJvbUJhc2VsaW5lIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAicGxvdC5kaXIiLCAicmVzdWx0cy5kaXIiLCAiZGF0YS5wcmVwLmRpciIpKSkKYGBgCgojIyMgUExJRVIgdHJhaW5lZCBvbiBTTEUgV0IgY29tcGVuZGl1bQoKYGBge3J9CnNsZS5iLmZpbGUgPC0gZmlsZS5wYXRoKGRhdGEucHJlcC5kaXIsICJFLUdFT0QtMzkwODhfU0xFLVdCX1BMSUVSX0lGTl9CLnRzdiIpCnNsZS5iLmRmIDwtIHJlYWRyOjpyZWFkX3RzdihzbGUuYi5maWxlKQpgYGAKCmBgYHtyfQojIFNMRSBwYXRpZW50cyBvbmx5Cmlmbi5iLmRmIDwtIHNsZS5iLmRmICU+JQogIGRwbHlyOjpmaWx0ZXIoZ3JlcGwoIlNMRSBwYXRpZW50IiwgUGF0aWVudCkpCgppZm4uYi5kZiA8LSBDYWxjdWxhdGVDaGFuZ2VGcm9tQmFzZWxpbmUoZGYgPSBhcy5kYXRhLmZyYW1lKGlmbi5iLmRmKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhcmlhYmxlLmlkID0gIkxWIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlLmlkID0gIlZhbHVlIikKZGYuZmlsZSA8LSBmaWxlLnBhdGgocmVzdWx0cy5kaXIsICJFLUdFT0QtMzkwODhfSUZOa19TTEVfUExJRVJfY2hhbmdlLnRzdiIpCnJlYWRyOjp3cml0ZV90c3YoaWZuLmIuZGYsIGRmLmZpbGUpCmBgYAoKYGBge3J9CnBsb3QuZmlsZSA8LSBmaWxlLnBhdGgocGxvdC5kaXIsICJFLUdFT0QtMzkwODhfSUZOa19TTEVfUExJRVJfY2hhbmdlLnBkZiIpClBsb3RDaGFuZ2VGcm9tQmFzZWxpbmUoZGYgPSBpZm4uYi5kZiwKICAgICAgICAgICAgICAgICAgICAgICB5LmxhYmVsID0gIkNoYW5nZSBpbiBMViB2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgcGxvdC50aXRsZSA9ICJTTEUgV0IgUExJRVIgLSBJRk4tSyB0cmVhdG1lbnQiLAogICAgICAgICAgICAgICAgICAgICAgIHBsb3QucGF0aCA9IHBsb3QuZmlsZSwKICAgICAgICAgICAgICAgICAgICAgICBmYWNldHMgPSAiTFYgfiBEYXkiKQpgYGAKCmBgYHtyfQpybShsaXN0ID0gc2V0ZGlmZihscygpLCBjKCIlPiUiLCAiQ2FsY3VsYXRlQ2hhbmdlRnJvbUJhc2VsaW5lIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAiUGxvdENoYW5nZUZyb21CYXNlbGluZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgInBsb3QuZGlyIiwgInJlc3VsdHMuZGlyIiwgImRhdGEucHJlcC5kaXIiKSkpCmBgYAoKIyMjIFBMSUVSIHRyYWluZWQgb24gYHJlY291bnQyYAoKYGBge3J9CnJlY291bnQuYi5maWxlIDwtIGZpbGUucGF0aChkYXRhLnByZXAuZGlyLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICJFLUdFT0QtMzkwODhfcmVjb3VudDJfUExJRVJfSUZOX0IudHN2IikKcmVjb3VudC5iLmRmIDwtIHJlYWRyOjpyZWFkX3RzdihyZWNvdW50LmIuZmlsZSkKYGBgCmBgYHtyfQojIFNMRSBwYXRpZW50cyBvbmx5Cmlmbi5iLmRmIDwtIHJlY291bnQuYi5kZiAlPiUKICBkcGx5cjo6ZmlsdGVyKGdyZXBsKCJTTEUgcGF0aWVudCIsIFBhdGllbnQpKQoKaWZuLmIuZGYgPC0gQ2FsY3VsYXRlQ2hhbmdlRnJvbUJhc2VsaW5lKGRmID0gYXMuZGF0YS5mcmFtZShpZm4uYi5kZiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXJpYWJsZS5pZCA9ICJMViIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZS5pZCA9ICJWYWx1ZSIpCmRmLmZpbGUgPC0gZmlsZS5wYXRoKHJlc3VsdHMuZGlyLCAiRS1HRU9ELTM5MDg4X0lGTmtfcmVjb3VudDJfUExJRVJfY2hhbmdlLnRzdiIpCnJlYWRyOjp3cml0ZV90c3YoaWZuLmIuZGYsIGRmLmZpbGUpCmBgYApgYGB7cn0KIyBwbG90CnBsb3QuZmlsZSA8LSBmaWxlLnBhdGgocGxvdC5kaXIsICJFLUdFT0QtMzkwODhfSUZOa19yZWNvdW50Ml9QTElFUl9jaGFuZ2UucGRmIikKUGxvdENoYW5nZUZyb21CYXNlbGluZShkZiA9IGlmbi5iLmRmLAogICAgICAgICAgICAgICAgICAgICAgIHkubGFiZWwgPSAiQ2hhbmdlIGluIExWIHZhbHVlIiwKICAgICAgICAgICAgICAgICAgICAgICBwbG90LnRpdGxlID0gInJlY291bnQyIFBMSUVSIC0gSUZOLUsgdHJlYXRtZW50IiwKICAgICAgICAgICAgICAgICAgICAgICBwbG90LnBhdGggPSBwbG90LmZpbGUsCiAgICAgICAgICAgICAgICAgICAgICAgZmFjZXRzID0gIkxWIH4gRGF5IikKYGBgCmBgYHtyfQpybShsaXN0ID0gc2V0ZGlmZihscygpLCBjKCIlPiUiLCAicmVzdWx0cy5kaXIiLCAicGxvdC5kaXIiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAiZGF0YS5wcmVwLmRpciIpKSkKYGBgCgojIyBBTUcgODExIAoKKipFLUdFT0QtNzgxOTM7IFdlbGNoZXIsIGV0IGFsLiAyMDE1LioqCgojIyMgUGxvdHRpbmcgRnVuY3Rpb24KCldlJ2xsIHdhbnQgdG8gZ2VuZXJhdGUgYSBib3hwbG90IHRoZSBpbnRlcmFjdGlvbiBiZXR3ZWVuIGRpc2Vhc2Ugc3RhdGUgKGUuZy4sCmhlYWx0aHksIFNMRSkgYW5kIHRpbWUgcG9pbnQgKGRheSBvZiB0cmlhbCkgZm9yIGVhY2ggb2YgdGhlIHRocmVlIG1ldGhvZHMuCldlJ2xsIHdyaXRlIGEgY3VzdG9tIHBsb3R0aW5nIGZ1bmN0aW9uIGZvciB0aGlzLgoKYGBge3J9ClBsb3RJbnRlcmFjdGlvbiA8LSBmdW5jdGlvbihkZiwgeS52YXIsIHdyYXAudmFyLCB5LmxhYmVsLCBwbG90LnRpdGxlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgcGxvdC5zdWJ0aXRsZSA9ICJXZWxjaGVyLCBldCBhbC4iKSB7CiAgIyBHaXZlbiBhIGRhdGEuZnJhbWUgdGhhdCBjb250YWlucyBhICJzdW1tYXJ5IiBleHByZXNzaW9uIGxldmVsIG9mIHNvbWUga2luZAogICMgZm9yIEUtR0VPRC03ODE5MyBzYW1wbGVzLCBtYWtlIGEgYm94cGxvdCB3aGVyZSB4L2dyb3VwcyBhcmUgCiAgIyBpbnRlcmFjdGlvbihEaXNlYXNlIHN0YXRlLCBEYXkpLiAgTm90IGludGVuZGVkIGZvciB1c2Ugb3V0c2lkZSB0aGlzIAogICMgY29udGV4dCAoJiBlbnYpIQogICMKICAjIEFyZ3M6CiAgIyAgIGRmOiBhIChsb25nIGZvcm0pIGRhdGEuZnJhbWUgY29udGFpbmluZyB0aGUgbWVhc3VyZW1lbnRzIGZvciBFLUdFT0QtNzgxOTMKICAjICAgeS52YXI6IHZhcmlhYmxlIHVzZWQgYXMgeS52YXIgKHN0cmluZzsgZXZhbHVhdGVkIHdpdGggZ2dwbG90Mjo6YWVzX3N0cmluZykKICAjICAgd3JhcC52YXI6IHN0cmluZyBwYXNzZWQgdG8gZ2dwbG90Mjo6ZmFjZXRfd3JhcDsgdXNlZCBmb3IgbXVsdGlwbGUgTFZzIAogICMgICAgICAgICAgICAgb3IgbW9kdWxlcwogICMgICB5LmxhYmVsOiBzdHJpbmcsIGxhYmVsIGZvciB5LWF4aXMgCiAgIyAgIHBsb3QudGl0bGU6IHN0cmluZywgcGxvdCB0aXRsZQogICMgICBwbG90LnN1YnRpdGxlOiBzdHJpbmcsIHBsb3Qgc3VidGl0bGU7IGRlZmF1bHQgIldlbGNoZXIsIGV0IGFsLiIKICAjCiAgIyBSZXR1cm5zOgogICMgICBnZ3Bsb3QyOjpnZ3Bsb3Qgb2JqZWN0CiAgCiAgZ2dwbG90Mjo6Z2dwbG90KGRmLCAKICAgICAgICAgICAgICAgICAgZ2dwbG90Mjo6YWVzKHggPSBpbnRlcmFjdGlvbihgRGlzZWFzZSBzdGF0ZWAsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYERheWApLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBpbnRlcmFjdGlvbihgRGlzZWFzZSBzdGF0ZWAsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGBEYXlgKSkpICsgCiAgICBnZ3Bsb3QyOjpnZW9tX2JveHBsb3QoZ2dwbG90Mjo6YWVzX3N0cmluZyh5ID0geS52YXIpKSArIAogICAgZ2dwbG90Mjo6Z2VvbV9wb2ludChnZ3Bsb3QyOjphZXNfc3RyaW5nKHkgPSB5LnZhciksCiAgICAgICAgICAgICAgICAgICAgICAgIGFscGhhID0gMC4zLCBwb3NpdGlvbiA9ICJqaXR0ZXIiKSArCiAgICBnZ3Bsb3QyOjpmYWNldF93cmFwKGFzLmZvcm11bGEocGFzdGUoIn4iLCB3cmFwLnZhcikpKSArCiAgICBnZ3Bsb3QyOjp0aGVtZV9idygpICsgCiAgICBnZ3Bsb3QyOjp0aGVtZShheGlzLnRleHQueCA9IGdncGxvdDI6OmVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpLAogICAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgICAgICAgICAgICAgICAgdGV4dCA9IGdncGxvdDI6OmVsZW1lbnRfdGV4dChzaXplID0gMTUpKSArCiAgICBnZ3Bsb3QyOjpsYWJzKHggPSAiaW50ZXJhY3Rpb24oRGlzZWFzZSBTdGF0ZSwgRGF5KSIsCiAgICAgICAgICAgICAgICAgIHkgPSB5LmxhYmVsLAogICAgICAgICAgICAgICAgICB0aXRsZSA9IHBsb3QudGl0bGUsCiAgICAgICAgICAgICAgICAgIHN1YnRpdGxlID0gcGxvdC5zdWJ0aXRsZSkgKwogICAgZ2dwbG90Mjo6c2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygic2VhZ3JlZW4zIiwgIiNkZWViZjciLCAiIzllY2FlMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIjMzE4MmJkIiwgIndoaXRlIikpICsKICAgIGdncGxvdDI6OnNjYWxlX3hfZGlzY3JldGUobGFiZWxzID0gYygiaGVhbHRoeSIsICJTTEUgYmFzZWxpbmUiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiU0xFIGRheSAxNSIsICJTTEUgZGF5IDU2IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlNMRSBFT1MiKSkKfQoKYGBgCgojIyMgTW9kdWxhciB0cmFuc2NyaXB0aW9uYWwgYW5hbHlzZXMKCmBgYHtyfQptb2QuZmlsZSA8LSBmaWxlLnBhdGgoZGF0YS5wcmVwLmRpciwKICAgICAgICAgICAgICAgICAgICAgICJFLUdFT0QtNzgxOTNfQ2hpY2hlX2V0X2FsX21vZHVsZS50c3YiKQptb2Quc3VtbWFyeS5kZiA8LSByZWFkcjo6cmVhZF90c3YobW9kLmZpbGUpCmBgYAoKYGBge3J9CnAgPC0gUGxvdEludGVyYWN0aW9uKGRmID0gbW9kLnN1bW1hcnkuZGYsCiAgICAgICAgICAgICAgICAgICAgIHkudmFyID0gIlN1bW1hcnkiLAogICAgICAgICAgICAgICAgICAgICB3cmFwLnZhciA9ICJNb2R1bGUiLAogICAgICAgICAgICAgICAgICAgICB5LmxhYmVsID0gIk1lYW4gZXhwcmVzc2lvbiBvZiBnZW5lcyBpbiBtb2R1bGUgKHBlciBzYW1wbGUpIiwKICAgICAgICAgICAgICAgICAgICAgcGxvdC50aXRsZSA9ICJJRk4gTW9kdWxhciBGcmFtZXdvcmsgRXhwcmVzc2lvbiAtIFRyZWF0bWVudCB3aXRoIEFNRyA4MTEiKQoKcGxvdC5maWxlIDwtIGZpbGUucGF0aChwbG90LmRpciwgIkUtR0VPRC03ODE5M19DaGljaGVfZXRfYWxfYm94cGxvdC5wZGYiKQpnZ3Bsb3QyOjpnZ3NhdmUocGxvdC5maWxlLCBwbG90ID0gcCwgd2lkdGggPSAxMSwgaGVpZ2h0ID0gNywgdW5pdHMgPSAiaW4iKQpgYGAKCmBgYHtyfQpybShsaXN0ID0gc2V0ZGlmZihscygpLCBjKCIlPiUiLCAicmVzdWx0cy5kaXIiLCAicGxvdC5kaXIiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAiZGF0YS5wcmVwLmRpciIsICJQbG90SW50ZXJhY3Rpb24iKSkpCmBgYAoKIyMjIFBMSUVSIHRyYWluZWQgb24gU0xFIFdCIGNvbXBlbmRpdW0KCmBgYHtyfQpzbGUuYi5maWxlIDwtIGZpbGUucGF0aChkYXRhLnByZXAuZGlyLCAiRS1HRU9ELTc4MTkzX1NMRS1XQl9QTElFUl9JRk5fQi50c3YiKQpzbGUuYi5kZiA8LSByZWFkcjo6cmVhZF90c3Yoc2xlLmIuZmlsZSkKYGBgCgpgYGB7cn0KcCA8LSBQbG90SW50ZXJhY3Rpb24oZGYgPSBzbGUuYi5kZiwKICAgICAgICAgICAgICAgICAgICAgeS52YXIgPSAiVmFsdWUiLAogICAgICAgICAgICAgICAgICAgICB3cmFwLnZhciA9ICJMViIsCiAgICAgICAgICAgICAgICAgICAgIHkubGFiZWwgPSAiTFYgdmFsdWUiLAogICAgICAgICAgICAgICAgICAgICBwbG90LnRpdGxlID0gIlNMRSBXQiBQTElFUiAtIFRyZWF0bWVudCB3aXRoIEFNRyA4MTEiKQoKcGxvdC5maWxlIDwtIGZpbGUucGF0aChwbG90LmRpciwgIkUtR0VPRC03ODE5M19TTEVfUExJRVJfYm94cGxvdC5wZGYiKQpnZ3Bsb3QyOjpnZ3NhdmUocGxvdC5maWxlLCBwbG90ID0gcCwgd2lkdGggPSAxMSwgaGVpZ2h0ID0gNywgdW5pdHMgPSAiaW4iKQpgYGAKCmBgYHtyfQpybShsaXN0ID0gc2V0ZGlmZihscygpLCBjKCIlPiUiLCAicmVzdWx0cy5kaXIiLCAicGxvdC5kaXIiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAiZGF0YS5wcmVwLmRpciIsICJQbG90SW50ZXJhY3Rpb24iKSkpCmBgYAoKIyMjIFBMSUVSIHRyYWluZWQgb24gYHJlY291bnQyYAoKYGBge3J9CnJlY291bnQuYi5maWxlIDwtIGZpbGUucGF0aChkYXRhLnByZXAuZGlyLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICJFLUdFT0QtNzgxOTNfcmVjb3VudDJfUExJRVJfSUZOX0IudHN2IikKcmVjb3VudC5iLmRmIDwtIHJlYWRyOjpyZWFkX3RzdihyZWNvdW50LmIuZmlsZSkKYGBgCmBgYHtyfQpwIDwtIFBsb3RJbnRlcmFjdGlvbihkZiA9IHJlY291bnQuYi5kZiwKICAgICAgICAgICAgICAgICAgICAgeS52YXIgPSAiVmFsdWUiLAogICAgICAgICAgICAgICAgICAgICB3cmFwLnZhciA9ICJMViIsCiAgICAgICAgICAgICAgICAgICAgIHkubGFiZWwgPSAiTFYgdmFsdWUiLAogICAgICAgICAgICAgICAgICAgICBwbG90LnRpdGxlID0gInJlY291bnQyIFBMSUVSIC0gVHJlYXRtZW50IHdpdGggQU1HIDgxMSIpCgpwbG90LmZpbGUgPC0gZmlsZS5wYXRoKHBsb3QuZGlyLCAiRS1HRU9ELTc4MTkzX3JlY291bnQyX1BMSUVSX2JveHBsb3QucGRmIikKZ2dwbG90Mjo6Z2dzYXZlKHBsb3QuZmlsZSwgcGxvdCA9IHAsIHdpZHRoID0gMTEsIGhlaWdodCA9IDcsIHVuaXRzID0gImluIikKYGBgCg==