Setup

library(ithi.utils)
load_base_libs()

library(methods)
library(forcats)
library(grid)
library(gridExtra)
library(gridBase)

library(ithi.meta)
library(ithi.figures)
library(ithi.utils)
library(ithi.seq)
library(ithi.clones)
library(ithi.supp)
library(ithi.xcr)
ihc_table_path <- snakemake@input$ihc_table
xcr_table_path <- snakemake@input$xcr_table
tcr_diversity_file <- snakemake@input$tcr_diversity
bcr_diversity_file <- snakemake@input$bcr_diversity

db_path <- snakemake@params$db
total_tiltypes <- snakemake@params$total_tiltypes
annotation_colours <- ithi.figures::get_annotation_colours()

ihc_table <- fread(ihc_table_path)
xcr_table <- read_clonotypes(xcr_table_path, duplicates = FALSE, db_path = db_path)

Read 16.4% of 304822 rows
Read 49.2% of 304822 rows
Read 85.3% of 304822 rows
Read 304822 rows and 18 (of 18) columns from 0.070 GB file in 00:00:06
tcr_diversity <- read_xcr_diversity_file(tcr_diversity_file, db_path)
bcr_diversity <- read_xcr_diversity_file(bcr_diversity_file, db_path)
xcr_diversity <- ithi.supp::get_xcr_diversity(tcr_diversity_file, bcr_diversity_file, 
    db_path, xcr_table)

Analysis

This is a good point and one worth validating. As you’ll see, the results will be ‘interesting’ to this reviewer, though perhaps not to us since we’re aware that the TIL responses can be quite polyclonal.

XCR_RANK_COLS <- c("#BE2730", "#CA3339", "#E8BA40", "#EFE74C", "#90BC59", "#35A75C", 
    "#2AACED", "#2573B4", "#2A2B55", "#6B3E8F", "#D8D8D8")
names(XCR_RANK_COLS) <- paste0("Rank_", as.character(1:11))

tcr_top <- ithi.xcr::top_clonotype_table(xcr_table, xcrtype = "TRB")
bcr_top <- ithi.xcr::top_clonotype_table(xcr_table, xcrtype = "IGH")

tcr_mat <- reshape2::acast(tcr_top, formula = rank ~ condensed_id, value.var = "freq", 
    fun.aggregate = sum)
bcr_mat <- reshape2::acast(bcr_top, formula = rank ~ condensed_id, value.var = "freq", 
    fun.aggregate = sum)
tcr_mat <- tcr_mat[rev(1:nrow(tcr_mat)), ]
bcr_mat <- bcr_mat[rev(1:nrow(bcr_mat)), ]

tcr_mat_melt <- melt(as.matrix(tcr_mat)) %>% plyr::rename(c(Var1 = "rank", Var2 = "condensed_id", 
    value = "proportion"))
bcr_mat_melt <- melt(as.matrix(bcr_mat)) %>% plyr::rename(c(Var1 = "rank", Var2 = "condensed_id", 
    value = "proportion"))

# tcr_mat_fill <- fill_cols(tcr_mat, condensed_ids) bcr_mat_fill <-
# fill_cols(bcr_mat, condensed_ids)
ihc_table_subset <- subset(ihc_table, select = c("condensed_id", "patient_id", 
    total_tiltypes))
na_samples <- ihc_table_subset %>% subset(select = -c(condensed_id, patient_id)) %>% 
    apply(1, function(x) any(is.na(x)))
ihc_table_subset <- ihc_table_subset[!na_samples, ]

ihc_table_melted <- melt(ihc_table_subset, id.vars = c("condensed_id", "patient_id"), 
    measure.vars = total_tiltypes, variable.name = "tiltype", value.name = "density")
ihc_table_melted$celltype <- ifelse(ihc_table_melted$tiltype %in% c("T_CD8_density", 
    "T_CD4_density"), yes = "T_cell", no = "B_cell")

ids_intersect <- intersect(ihc_table_melted$condensed_id, tcr_mat_melt$condensed_id)
ihc_table_melted <- subset(ihc_table_melted, condensed_id %in% ids_intersect)
tcr_mat_melt <- subset(tcr_mat_melt, condensed_id %in% ids_intersect)
bcr_mat_melt <- subset(bcr_mat_melt, condensed_id %in% ids_intersect)

ihc_celltype_totals <- ihc_table_melted %>% group_by(condensed_id) %>% summarise(t_total = sum(density[celltype == 
    "T_cell"]), b_total = sum(density[celltype == "B_cell"]))
t_sample_order <- with(ihc_celltype_totals, condensed_id[order(t_total, decreasing = FALSE)])
b_sample_order <- with(ihc_celltype_totals, condensed_id[order(b_total, decreasing = FALSE)])

plot_stacked_xcr <- function(xcr_mat_melt, sample_order, label_x = FALSE) {
    xcr_mat_melt$condensed_id <- factor(xcr_mat_melt$condensed_id, levels = sample_order)
    p <- ggplot(xcr_mat_melt, aes(x = condensed_id, y = proportion)) + geom_bar(aes(fill = forcats::fct_rev(rank)), 
        stat = "identity") + theme_bw() + ithi.utils::theme_Publication() + 
        ithi.utils::theme_nature() + scale_fill_manual(values = XCR_RANK_COLS) + 
        scale_y_continuous(expand = c(0, 0)) + guides(fill = FALSE) + theme(axis.text.x = element_blank()) + 
        ithi.utils::ggmargins(type = "topminus")
    if (!label_x) {
        return(p + xlab(""))
    } else {
        return(p)
    }
}

p1 <- plot_stacked_xcr(tcr_mat_melt, sample_order = t_sample_order, label_x = FALSE)
p2 <- plot_stacked_xcr(bcr_mat_melt, sample_order = b_sample_order, label_x = FALSE)


celltypes <- c("T_cell", "B_cell")

create_tilplots <- function(ihc_table_melted, sample_order, selected_celltype) {
    dat <- subset(ihc_table_melted, celltype == selected_celltype)
    dat$condensed_id <- factor(dat$condensed_id, levels = sample_order)
    dat$patient_id <- ithi.meta::factor_id(dat$patient_id, type = "patient_id", 
        db_path)
    dat <- dat[order(dat$condensed_id), ]
    subtils <- dat$tiltype %>% unique
    subplots <- lapply(subtils, function(y) {
        p <- ggplot(dat %>% subset(tiltype == y), aes(x = condensed_id, y = density)) + 
            geom_bar(aes(fill = patient_id), stat = "identity") + theme_bw() + 
            theme_Publication() + theme_nature() + scale_y_continuous(expand = c(0, 
            0)) + scale_fill_manual(values = annotation_colours$patient_id) + 
            theme(axis.text.x = element_blank()) + xlab("") + ylab(y) + guides(fill = FALSE) + 
            ithi.utils::ggmargins(type = "topminus")
        return(p)
    })
    names(subplots) <- subtils
    return(subplots)
}

t_tilplots <- create_tilplots(ihc_table_melted, sample_order = t_sample_order, 
    selected_celltype = "T_cell")
b_tilplots <- create_tilplots(ihc_table_melted, sample_order = b_sample_order, 
    selected_celltype = "B_cell")
tilplots <- list(T_cell = t_tilplots, B_cell = b_tilplots)

tcr_diversity_table <- tcr_diversity %>% plyr::rename(c(observedDiversity_mean = "Unique clonotypes", 
    shannonWienerIndex_mean = "Entropy"))
bcr_diversity_table <- bcr_diversity %>% plyr::rename(c(observedDiversity_mean = "Unique clonotypes", 
    shannonWienerIndex_mean = "Entropy"))

plot_xcr_diversity <- function(xcr_diversity_table, sample_order, diversity_measures = c("Unique clonotypes", 
    "Entropy")) {
    xcr_diversity_table <- subset(xcr_diversity_table, condensed_id %in% sample_order)
    diversity_table_melted <- melt(xcr_diversity_table, id.vars = c("condensed_id", 
        "patient_id"), measure.vars = diversity_measures, variable.name = "measure", 
        value.name = "diversity")
    diversity_table_melted$condensed_id <- factor(diversity_table_melted$condensed_id, 
        levels = sample_order)
    subplots <- lapply(diversity_measures, function(divtype) {
        dat <- subset(diversity_table_melted, measure == divtype)
        dat$patient_id <- ithi.meta::factor_id(dat$patient_id, type = "patient_id", 
            db_path)
        
        ggplot(dat, aes(x = condensed_id, y = diversity)) + geom_bar(aes(fill = patient_id), 
            stat = "identity") + theme_bw() + theme_Publication() + theme_nature() + 
            scale_y_continuous(expand = c(0, 0)) + scale_fill_manual(values = annotation_colours$patient_id) + 
            theme(axis.text.x = element_blank()) + xlab("") + ylab(divtype) + 
            guides(fill = FALSE) + ithi.utils::ggmargins(type = "topminus")
    })
    names(subplots) <- diversity_measures
    return(subplots)
}

tcrdivplots <- plot_xcr_diversity(tcr_diversity_table, sample_order = t_sample_order)
bcrdivplots <- plot_xcr_diversity(bcr_diversity_table, sample_order = b_sample_order)

construct_combined_plot <- function(celltype) {
    if (celltype == "T_cell") {
        raw_gplots <- list(tilplots$T_cell$T_CD8_density, tilplots$T_cell$T_CD4_density, 
            p1, tcrdivplots$`Unique clonotypes`, tcrdivplots$Entropy)
    } else if (celltype == "B_cell") {
        raw_gplots <- list(tilplots$B_cell$T_CD20_density, tilplots$B_cell$T_Plasma_density, 
            p2, bcrdivplots$`Unique clonotypes`, bcrdivplots$Entropy)
    }
    
    grob_gplots <- lapply(raw_gplots, function(x) ggplotGrob(x))
    combined_grob <- do.call(gridExtra::rbind.gtable, grob_gplots)
    return(combined_grob)
}

t_combined_plot <- construct_combined_plot(celltype = "T_cell")
b_combined_plot <- construct_combined_plot(celltype = "B_cell")

TCR

grid.newpage()
grid.draw(t_combined_plot)

In general, we can see that higher CD8/CD4 T cell density is accompanied by an increase in the fraction of clonotype reads accounted for by rare clonotypes. In some samples/patients (e.g. patient 15) there appears to be ‘expansion’ of the most dominat clonotype(s), but if you look at the patient 15 sample with low TIL density (in the left half of the plot, near the middle) it becomes apparent that samples with lower T cell density have a higher proportion of clonotypes made up by rare clonotypes.

Therefore, it would be correct to say that the TIL responses in high-TIL density samples are polyclonal, rather than dominated by a single clone.

As you can see from the last two barplots, our diversity measures are capturing the observation that lower proportion accounted for by top 10 clonotypes = higher TCR diversity.

BCR

grid.newpage()
grid.draw(b_combined_plot)

The same isn’t as true for BCRs – we can see evidence that samples with the highest TIL densities are not necessarily polyclonal. This is in agreement with our observation that BCR clonality does not correlate with B-cell densities.

TODO: IDEALLY SHOW UNCERTAINTY IN XCR BARS TOO (using the estimates for TCR/BCR).

TODO: Relabel axis names, add colour legend for patients. Discuss if best way to show patients is with coloured bars or with a separate annotation track.

As you can see, it’s pretty obvious that the diversity measures are doing a good job capturing the distribution of top clonotypes.

LS0tCnRpdGxlOiAiVElMIGRlbnNpdHkgLSBYQ1IgZXhwYW5zaW9uIgotLS0KICAgICAgICAgICAgICAgICAgICAgICAgYGBge3IsIGVjaG89RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CgojIyMjIyMjIyBTbmFrZW1ha2UgaGVhZGVyICMjIyMjIyMjCmxpYnJhcnkobWV0aG9kcykKU25ha2VtYWtlIDwtIHNldENsYXNzKAogICAgIlNuYWtlbWFrZSIsCiAgICBzbG90cyA9IGMoCiAgICAgICAgaW5wdXQgPSAibGlzdCIsCiAgICAgICAgb3V0cHV0ID0gImxpc3QiLAogICAgICAgIHBhcmFtcyA9ICJsaXN0IiwKICAgICAgICB3aWxkY2FyZHMgPSAibGlzdCIsCiAgICAgICAgdGhyZWFkcyA9ICJudW1lcmljIiwKICAgICAgICBsb2cgPSAibGlzdCIsCiAgICAgICAgcmVzb3VyY2VzID0gImxpc3QiLAogICAgICAgIGNvbmZpZyA9ICJsaXN0IiwKICAgICAgICBydWxlID0gImNoYXJhY3RlciIKICAgICkKKQpzbmFrZW1ha2UgPC0gU25ha2VtYWtlKAogICAgaW5wdXQgPSBsaXN0KCcvc2hhaGxhYi9hbHpoYW5nL3BpcGVsaW5lX291dHB1dHMvaXRoX2ltbXVuZS9taXhjci9taXhjcl9ydW5zL2l0aF8xXzJfMy9taXhjcjUvcG9zdHByb2Nlc3MvVFJCL3Bvc3RmaWx0ZXJfZGl2ZXJzaXR5X3N0YXRzL2RpdmVyc2l0eS5zdHJpY3QucmVzYW1wbGVkLnR4dCcsICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy90YWJsZXMvcnVuMi9paGNfdGFibGUudHN2JywgJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9yZXN1bHRzL3RhYmxlcy9ydW4yL3hjcl90YWJsZS50c3YnLCAnL3NoYWhsYWIvYWx6aGFuZy9waXBlbGluZV9vdXRwdXRzL2l0aF9pbW11bmUvbWl4Y3IvbWl4Y3JfcnVucy9pdGhfMV8yXzMvbWl4Y3I1L3Bvc3Rwcm9jZXNzL0lHSC9wb3N0ZmlsdGVyX2RpdmVyc2l0eV9zdGF0cy9kaXZlcnNpdHkuc3RyaWN0LnJlc2FtcGxlZC50eHQnLCAnbm90ZWJvb2tzL3RpbGRlbnNpdHlfeGNyZXhwYW5zaW9uLlJtZCcsICJub3RlYm9vayIgPSAnbm90ZWJvb2tzL3RpbGRlbnNpdHlfeGNyZXhwYW5zaW9uLlJtZCcsICJpaGNfdGFibGUiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9yZXN1bHRzL3RhYmxlcy9ydW4yL2loY190YWJsZS50c3YnLCAieGNyX3RhYmxlIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy90YWJsZXMvcnVuMi94Y3JfdGFibGUudHN2JywgImJjcl9kaXZlcnNpdHkiID0gJy9zaGFobGFiL2FsemhhbmcvcGlwZWxpbmVfb3V0cHV0cy9pdGhfaW1tdW5lL21peGNyL21peGNyX3J1bnMvaXRoXzFfMl8zL21peGNyNS9wb3N0cHJvY2Vzcy9JR0gvcG9zdGZpbHRlcl9kaXZlcnNpdHlfc3RhdHMvZGl2ZXJzaXR5LnN0cmljdC5yZXNhbXBsZWQudHh0JywgInRjcl9kaXZlcnNpdHkiID0gJy9zaGFobGFiL2FsemhhbmcvcGlwZWxpbmVfb3V0cHV0cy9pdGhfaW1tdW5lL21peGNyL21peGNyX3J1bnMvaXRoXzFfMl8zL21peGNyNS9wb3N0cHJvY2Vzcy9UUkIvcG9zdGZpbHRlcl9kaXZlcnNpdHlfc3RhdHMvZGl2ZXJzaXR5LnN0cmljdC5yZXNhbXBsZWQudHh0JyksCiAgICBvdXRwdXQgPSBsaXN0KCcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy9yZXZpZXcvbm90ZWJvb2tzL3J1bjIvdGlsZGVuc2l0eV94Y3JleHBhbnNpb24ubmIuaHRtbCcpLAogICAgcGFyYW1zID0gbGlzdChjKCdUX0NEOF9kZW5zaXR5JywgJ1RfQ0Q0X2RlbnNpdHknLCAnVF9DRDIwX2RlbnNpdHknLCAnVF9QbGFzbWFfZGVuc2l0eScpLCAndGlsZGVuc2l0eV94Y3JleHBhbnNpb25fYW5hbHlzaXMnLCAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL21ldGFkYXRhL2RiL2ltbXVuZV9wcm9qZWN0LnNxbGl0ZTMnLCAidG90YWxfdGlsdHlwZXMiID0gYygnVF9DRDhfZGVuc2l0eScsICdUX0NENF9kZW5zaXR5JywgJ1RfQ0QyMF9kZW5zaXR5JywgJ1RfUGxhc21hX2RlbnNpdHknKSwgImRiIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvbWV0YWRhdGEvZGIvaW1tdW5lX3Byb2plY3Quc3FsaXRlMycsICJuYW1lIiA9ICd0aWxkZW5zaXR5X3hjcmV4cGFuc2lvbl9hbmFseXNpcycpLAogICAgd2lsZGNhcmRzID0gbGlzdCgpLAogICAgdGhyZWFkcyA9IDEsCiAgICBsb2cgPSBsaXN0KCcvc2hhaGxhYi9hbHpoYW5nL2NsdXN0dG1wL3BhcGVycmV2aWV3Mi9ub3RlYm9va3MvdGlsZGVuc2l0eV94Y3JleHBhbnNpb25fYW5hbHlzaXMubG9nJyksCiAgICByZXNvdXJjZXMgPSBsaXN0KCksCiAgICBjb25maWcgPSBsaXN0KCJwcmV2YWxlbmNlX3RocmVzaG9sZCIgPSAwLjAxLCAicGF0aWVudHNfZm9yX2Nsb25hbCIgPSBjKDEsIDIsIDMsIDQsIDcsIDksIDEwLCAxMSwgMTIsIDEzLCAxNCwgMTUsIDE2LCAxNyksICJ0YWJsZV9kaXIiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9yZXN1bHRzL3Jldmlldy90YWJsZXMvcnVuMicsICJpY2djX3N1YnR5cGVzIiA9ICcvc2hhaGxhYi9hbHpoYW5nL2RhdGEvSUNHQy9pY2djX3ByaW1hcnlfdHVtb3VyX3N1YnR5cGVzLnRzdicsICJtbWN0bV9maW5hbF9wYXRpZW50X2RpciIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3Jlc3VsdHMvbW1jdG1fcmVzdWx0cy9pdGhfYnktcGF0aWVudF93aXRoLW92JywgImloY190YWJsZSIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3BhcGVyL3Jlc3VsdHMvdGFibGVzL3J1bjIvaWhjX3RhYmxlLnRzdicsICJjbG9uZV90cmVlcyIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3BhcGVyL3Jlc3VsdHMvdGFibGVzL3J1bjIvY2xvbmVzL3RyZWVfZGF0YS50c3YnLCAic252X2NsdXN0ZXJfZGlyIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy90YWJsZXMvcnVuMi9jbG9uZXMvc252X2NsdXN0ZXInLCAidHVtb3VyX3B1cml0eSIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3BhcGVyL3Jlc3VsdHMvdGFibGVzL3J1bjIvdHVtb3VyX3B1cml0eS50c3YnLCAiaWdwYXJ0aXRpb25fb3V0ZGlyIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3BpcGVsaW5lX291dHB1dHMvaXRoX2ltbXVuZS9pZ3BhcnRpdGlvbi9ydW4yMicsICJyb29uZXlfbXV0c2lnY3ZfZmlsZSIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL2V4dGVybmFsL290aGVyX3BhcGVycy9tbWM2Lnhsc3gnLCAiZGIiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9tZXRhZGF0YS9kYi9pbW11bmVfcHJvamVjdC5zcWxpdGUzJywgImNsb25lX2JyYW5jaF9sZW5ndGhzIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy90YWJsZXMvcnVuMi9jbG9uZXMvYnJhbmNoX2RhdGEudHN2JywgImJjcl9kaXZlcnNpdHkiID0gJy9zaGFobGFiL2FsemhhbmcvcGlwZWxpbmVfb3V0cHV0cy9pdGhfaW1tdW5lL21peGNyL21peGNyX3J1bnMvaXRoXzFfMl8zL21peGNyNS9wb3N0cHJvY2Vzcy9JR0gvcG9zdGZpbHRlcl9kaXZlcnNpdHlfc3RhdHMvZGl2ZXJzaXR5LnN0cmljdC5yZXNhbXBsZWQudHh0JywgInRpbHNfZm9yX2NsdXN0ZXIiID0gYygnRV9DRDhfZGVuc2l0eScsICdFX0NENF9kZW5zaXR5JywgJ0VfQ0QyMF9kZW5zaXR5JywgJ0VfUGxhc21hX2RlbnNpdHknLCAnU19DRDhfZGVuc2l0eScsICdTX0NENF9kZW5zaXR5JywgJ1NfQ0QyMF9kZW5zaXR5JywgJ1NfUGxhc21hX2RlbnNpdHknKSwgImNsb2xhX3Jlc3VsdF9maWxlIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3BpcGVsaW5lX291dHB1dHMvaXRoX2ltbXVuZS9jbG9sYS9ydW40L2Nsb2xhX2NvbmRlbnNlZF9yZXN1bHRzL2JldGEvY2xvbGFfcmVzdWx0cy50c3YnLCAidG90YWxfdGlsdHlwZXMiID0gYygnVF9DRDhfZGVuc2l0eScsICdUX0NENF9kZW5zaXR5JywgJ1RfQ0QyMF9kZW5zaXR5JywgJ1RfUGxhc21hX2RlbnNpdHknKSwgImJlbmNobWFya2RpciIgPSAnL3NoYWhsYWIvYWx6aGFuZy9iZW5jaG1hcmtzL3BhcGVycmV2aWV3MicsICJzb21hdGljX2NvZGluZ19yZXN1bHRfZGlyIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy90YWJsZXMvcnVuMi9zb21hdGljX2NvZGluZ192YXJpYW50cycsICJhbGxfdGlsdHlwZXMiID0gYygnVF9DRDhfZGVuc2l0eScsICdUX0NENF9kZW5zaXR5JywgJ1RfQ0QyMF9kZW5zaXR5JywgJ1RfUGxhc21hX2RlbnNpdHknLCAnRV9DRDhfZGVuc2l0eScsICdFX0NENF9kZW5zaXR5JywgJ0VfQ0QyMF9kZW5zaXR5JywgJ0VfUGxhc21hX2RlbnNpdHknLCAnU19DRDhfZGVuc2l0eScsICdTX0NENF9kZW5zaXR5JywgJ1NfQ0QyMF9kZW5zaXR5JywgJ1NfUGxhc21hX2RlbnNpdHknKSwgImZpbm5oZV9waXBlbGluZV9yZXN1bHRzX2RpciIgPSAnL3NoYWhsYWIvYWx6aGFuZy9waXBlbGluZV9vdXRwdXRzL2l0aF9pbW11bmUvZmlubmhlL3J1bjEnLCAibmFub3N0cmluZ19kYXRhIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcmVzdWx0cy9uYW5vc3RyaW5nX3Jlc3VsdHMvaXRoX2Z1bGwvcWMvbGltbWFfcXVhbnRpbGUvbm9ybWFsaXplZF9leHByZXNzaW9uX3ZvYV9sYWJlbHNfZmlsdGVyZWQudHN2JywgInNudl90YWJsZSIgPSAnL3NoYWhsYWIvYW1jcGhlcnNvbi9wcm9qZWN0cy9pdGgzL2l0aDMvbm90ZWJvb2tzL2Jlc3Bva2UvaXRoX3NudnMudHN2JywgImtub3duX3N1YnR5cGVzX2FycmF5IiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvZGF0YS9leHByZXNzaW9uL2FycmF5L3N1YnR5cGVzL2tub3duX3N1YnR5cGVzLnRzdicsICJub3RlYm9va19kaXIiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9yZXN1bHRzL3Jldmlldy9ub3RlYm9va3MvcnVuMicsICJ0Y3JfZGl2ZXJzaXR5IiA9ICcvc2hhaGxhYi9hbHpoYW5nL3BpcGVsaW5lX291dHB1dHMvaXRoX2ltbXVuZS9taXhjci9taXhjcl9ydW5zL2l0aF8xXzJfMy9taXhjcjUvcG9zdHByb2Nlc3MvVFJCL3Bvc3RmaWx0ZXJfZGl2ZXJzaXR5X3N0YXRzL2RpdmVyc2l0eS5zdHJpY3QucmVzYW1wbGVkLnR4dCcsICJpbWFnZV9zdW1tYXJ5MiIgPSAnL3NoYWhsYWIvYWx6aGFuZy9kYXRhL2l0aGkveXVhbl9oZWNyX2ltYWdlX3Jlc3VsdHNfMi5jc3YnLCAibmVvZWRpdGluZ19vdXRkaXIiID0gJy9zaGFobGFiL2FsemhhbmcvcGlwZWxpbmVfb3V0cHV0cy9pdGhfaW1tdW5lL25lb2VkaXRpbmcvcnVuNicsICJhcnJheV9leHByZXNzaW9uX2ZpbGUiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9kYXRhL2V4cHJlc3Npb24vYXJyYXkvZ2VuZV9leHByc19ybWFfYmF0Y2hfY29ycmVjdGVkLnR4dCcsICJ0aWxfY2x1c3RlcnNfb3V0cHV0IiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy9pbnRlcm1lZGlhdGVzL3J1bjIvdGlsX2NsdXN0ZXJzX291dHB1dC50eHQnLCAiaXRoX3N0YXRzIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy90YWJsZXMvcnVuMi9pdGhfc3RhdGlzdGljcy50c3YnLCAiaGVfcmVzdWx0c19kaXIiID0gJy9zaGFobGFiL2FsemhhbmcvZGF0YS9pdGhpL2Zpbm5fcmVzdWx0cy9oZV9vdXRwdXRfTm92MjknLCAibmFub3N0cmluZ19hbm5vdGF0aW9ucyIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL2RhdGEvZXhwcmVzc2lvbi9uYW5vc3RyaW5nL3BhbmNhbmNlcl9hbm5vdGF0aW9ucy50c3YnLCAiY2xvbmVfcHJldmFsZW5jZXMiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9yZXN1bHRzL3RhYmxlcy9ydW4yL2Nsb25lcy9jbG9uZV9kYXRhLnRzdicsICJ2YXJpYWJpbGl0eV90eXBlIiA9ICdzdGFiaWxpemUnLCAibG9nZGlyIiA9ICcvc2hhaGxhYi9hbHpoYW5nL2NsdXN0dG1wL3BhcGVycmV2aWV3MicsICJjb3B5bnVtYmVyX3RhYmxlIiA9ICcvc2hhaGxhYi9hbHpoYW5nL2RhdGEvaXRoaS9tYXN0ZXJfY29weW51bWJlcl9maWxlLnRzdicsICJtb2xzdWJ0eXBlcyIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3BhcGVyL3Jlc3VsdHMvdGFibGVzL3J1bjIvbW9sc3VidHlwZXMudHN2JywgIml0aF9zdGF0X3R5cGVzIiA9IGMoJ2VudHJvcHknLCAncG9zdHByb2Nlc3NlZF9kaXZlcmdlbmNlJywgJ2NvbWJpbmVkX2l0aF9ub3JtYWxpemVkJywgJ3Byb3BvcnRpb25fc3ViY2xvbmFsJyksICJyZW1peHRfY2VsbHVsYXJpdHlfcGxvaWR5IiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy90YWJsZXMvcnVuMi9yZW1peHRfY2VsbHVsYXJpdHlfcGxvaWR5LnRzdicsICJpY2djX3NwZWNpbWVuIiA9ICcvc2hhaGxhYi9hbHpoYW5nL2RhdGEvSUNHQy9zcGVjaW1lbi50c3YnLCAiaXRoX2ljZ2NfYmMiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9yZXN1bHRzL3RhYmxlcy9ydW4yL2l0aF9pY2djX21lcmdlZF9iYy50c3YnLCAidGlsY2x1c3Rlcl9zdXBlcnZpc2VkX2lweW5iIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmV2aWV3L2lweS90aWxjbHVzdGVyX3N1cGVydmlzZWRtdWx0aWNsYXNzLmlweW5iJywgInRpbHNfZm9yX3ZhcmlhYmlsaXR5IiA9IGMoJ1RfQ0Q4X2RlbnNpdHknLCAnVF9DRDRfZGVuc2l0eScsICdUX0NEMjBfZGVuc2l0eScsICdUX1BsYXNtYV9kZW5zaXR5JyksICJicmVha3BvaW50X3RhYmxlIiA9ICcvc2hhaGxhYi9hbWNwaGVyc29uL3Byb2plY3RzL2l0aDMvaXRoMy9ub3RlYm9va3MvYmVzcG9rZS9pdGhfYnJlYWtwb2ludHMudHN2JywgImRpc3RhbmNlX21ldGhvZCIgPSAnaG9ybicsICJ4Y3JfdGFibGUiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9yZXN1bHRzL3RhYmxlcy9ydW4yL3hjcl90YWJsZS50c3YnLCAicmVmc2VxX2dlbmVfZmlsZSIgPSAnL3NoYWhsYWIvYWx6aGFuZy9kYXRhL2dlbm9tZS9oZzE5L3JlZnNlcV9nZW5lcy5iZWQnLCAiZXBpdG9wZXNfdW5pcXVlX2ZpbHRlcmVkIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy90YWJsZXMvcnVuMi9lcGl0b3Blc191bmlxdWVfZmlsdGVyZWQudHN2JywgImloY19mZWF0dXJlc19vdXRwdXQiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9yZXN1bHRzL2ludGVybWVkaWF0ZXMvcnVuMi9paGNfZmVhdHVyZXNfb3V0cHV0LnR4dCcsICJpbWFnZV9zdW1tYXJ5IiA9ICcvc2hhaGxhYi9hbHpoYW5nL2RhdGEvaXRoaS95dWFuX2hlY3JfaW1hZ2VfcmVzdWx0cy5jc3YnKSwKICAgIHJ1bGUgPSAndGlsZGVuc2l0eV94Y3JleHBhbnNpb25fYW5hbHlzaXMnCikKIyMjIyMjIyMgT3JpZ2luYWwgc2NyaXB0ICMjIyMjIyMjIwoKICAgICAgICAgICAgICAgICAgICAgICAgYGBgCgoKIyMgU2V0dXAKCmBgYHtyIGdsb2JhbF9jaHVua19vcHRpb25zLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIHRpZHk9VFJVRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgY2FjaGU9VFJVRSkgI2NhY2hlPVRSVUUKYGBgCgpgYGB7cn0KbGlicmFyeShpdGhpLnV0aWxzKQpsb2FkX2Jhc2VfbGlicygpCgpsaWJyYXJ5KG1ldGhvZHMpCmxpYnJhcnkoZm9yY2F0cykKbGlicmFyeShncmlkKQpsaWJyYXJ5KGdyaWRFeHRyYSkKbGlicmFyeShncmlkQmFzZSkKCmxpYnJhcnkoaXRoaS5tZXRhKQpsaWJyYXJ5KGl0aGkuZmlndXJlcykKbGlicmFyeShpdGhpLnV0aWxzKQpsaWJyYXJ5KGl0aGkuc2VxKQpsaWJyYXJ5KGl0aGkuY2xvbmVzKQpsaWJyYXJ5KGl0aGkuc3VwcCkKbGlicmFyeShpdGhpLnhjcikKYGBgCgpgYGB7cn0KaWhjX3RhYmxlX3BhdGggPC0gc25ha2VtYWtlQGlucHV0JGloY190YWJsZQp4Y3JfdGFibGVfcGF0aCA8LSBzbmFrZW1ha2VAaW5wdXQkeGNyX3RhYmxlCnRjcl9kaXZlcnNpdHlfZmlsZSA8LSBzbmFrZW1ha2VAaW5wdXQkdGNyX2RpdmVyc2l0eQpiY3JfZGl2ZXJzaXR5X2ZpbGUgPC0gc25ha2VtYWtlQGlucHV0JGJjcl9kaXZlcnNpdHkKCmRiX3BhdGggPC0gc25ha2VtYWtlQHBhcmFtcyRkYgp0b3RhbF90aWx0eXBlcyA8LSBzbmFrZW1ha2VAcGFyYW1zJHRvdGFsX3RpbHR5cGVzCmBgYAoKYGBge3J9CmFubm90YXRpb25fY29sb3VycyA8LSBpdGhpLmZpZ3VyZXM6OmdldF9hbm5vdGF0aW9uX2NvbG91cnMoKQoKaWhjX3RhYmxlIDwtIGZyZWFkKGloY190YWJsZV9wYXRoKQp4Y3JfdGFibGUgPC0gcmVhZF9jbG9ub3R5cGVzKHhjcl90YWJsZV9wYXRoLCBkdXBsaWNhdGVzID0gRkFMU0UsIGRiX3BhdGggPSBkYl9wYXRoKQoKdGNyX2RpdmVyc2l0eSA8LSByZWFkX3hjcl9kaXZlcnNpdHlfZmlsZSh0Y3JfZGl2ZXJzaXR5X2ZpbGUsIGRiX3BhdGgpCmJjcl9kaXZlcnNpdHkgPC0gcmVhZF94Y3JfZGl2ZXJzaXR5X2ZpbGUoYmNyX2RpdmVyc2l0eV9maWxlLCBkYl9wYXRoKQp4Y3JfZGl2ZXJzaXR5IDwtIGl0aGkuc3VwcDo6Z2V0X3hjcl9kaXZlcnNpdHkodGNyX2RpdmVyc2l0eV9maWxlLCBiY3JfZGl2ZXJzaXR5X2ZpbGUsIGRiX3BhdGgsIHhjcl90YWJsZSkKYGBgCgojIyBBbmFseXNpcwoKVGhpcyBpcyBhIGdvb2QgcG9pbnQgYW5kIG9uZSB3b3J0aCB2YWxpZGF0aW5nLiBBcyB5b3UnbGwgc2VlLCB0aGUgcmVzdWx0cyB3aWxsIGJlICdpbnRlcmVzdGluZycgdG8gdGhpcyByZXZpZXdlciwgdGhvdWdoIHBlcmhhcHMgbm90IHRvIHVzIHNpbmNlIHdlJ3JlIGF3YXJlIHRoYXQgdGhlIFRJTCByZXNwb25zZXMgY2FuIGJlIHF1aXRlIHBvbHljbG9uYWwuIAoKYGBge3J9ClhDUl9SQU5LX0NPTFMgPC0gYygiI0JFMjczMCIsCiAgICAgICAgICAgICAgICAgICAgICIjQ0EzMzM5IiwKICAgICAgICAgICAgICAgICAgICAgIiNFOEJBNDAiLAogICAgICAgICAgICAgICAgICAgICAiI0VGRTc0QyIsCiAgICAgICAgICAgICAgICAgICAgICIjOTBCQzU5IiwKICAgICAgICAgICAgICAgICAgICAgIiMzNUE3NUMiLAogICAgICAgICAgICAgICAgICAgICAiIzJBQUNFRCIsCiAgICAgICAgICAgICAgICAgICAgICIjMjU3M0I0IiwKICAgICAgICAgICAgICAgICAgICAgIiMyQTJCNTUiLAogICAgICAgICAgICAgICAgICAgICAiIzZCM0U4RiIsCiAgICAgICAgICAgICAgICAgICAgICIjRDhEOEQ4IikKbmFtZXMoWENSX1JBTktfQ09MUykgPC0gcGFzdGUwKCJSYW5rXyIsIGFzLmNoYXJhY3RlcigxOjExKSkKCnRjcl90b3AgPC0gaXRoaS54Y3I6OnRvcF9jbG9ub3R5cGVfdGFibGUoeGNyX3RhYmxlLCB4Y3J0eXBlID0gIlRSQiIpCmJjcl90b3AgPC0gaXRoaS54Y3I6OnRvcF9jbG9ub3R5cGVfdGFibGUoeGNyX3RhYmxlLCB4Y3J0eXBlID0gIklHSCIpCgp0Y3JfbWF0IDwtIHJlc2hhcGUyOjphY2FzdCh0Y3JfdG9wLCBmb3JtdWxhID0gcmFuayB+IGNvbmRlbnNlZF9pZCwgdmFsdWUudmFyID0gImZyZXEiLCBmdW4uYWdncmVnYXRlID0gc3VtKQpiY3JfbWF0IDwtIHJlc2hhcGUyOjphY2FzdChiY3JfdG9wLCBmb3JtdWxhID0gcmFuayB+IGNvbmRlbnNlZF9pZCwgdmFsdWUudmFyID0gImZyZXEiLCBmdW4uYWdncmVnYXRlID0gc3VtKQp0Y3JfbWF0IDwtIHRjcl9tYXRbcmV2KDE6bnJvdyh0Y3JfbWF0KSksXQpiY3JfbWF0IDwtIGJjcl9tYXRbcmV2KDE6bnJvdyhiY3JfbWF0KSksXQoKdGNyX21hdF9tZWx0IDwtIG1lbHQoYXMubWF0cml4KHRjcl9tYXQpKSAlPiUgcGx5cjo6cmVuYW1lKGMoJ1ZhcjEnPSdyYW5rJywgJ1ZhcjInPSdjb25kZW5zZWRfaWQnLCAndmFsdWUnPSdwcm9wb3J0aW9uJykpCmJjcl9tYXRfbWVsdCA8LSBtZWx0KGFzLm1hdHJpeChiY3JfbWF0KSkgJT4lIHBseXI6OnJlbmFtZShjKCdWYXIxJz0ncmFuaycsICdWYXIyJz0nY29uZGVuc2VkX2lkJywgJ3ZhbHVlJz0ncHJvcG9ydGlvbicpKQoKI3Rjcl9tYXRfZmlsbCA8LSBmaWxsX2NvbHModGNyX21hdCwgY29uZGVuc2VkX2lkcykKI2Jjcl9tYXRfZmlsbCA8LSBmaWxsX2NvbHMoYmNyX21hdCwgY29uZGVuc2VkX2lkcykKYGBgCgpgYGB7cn0KaWhjX3RhYmxlX3N1YnNldCA8LSBzdWJzZXQoaWhjX3RhYmxlLCBzZWxlY3QgPSBjKCJjb25kZW5zZWRfaWQiLCAicGF0aWVudF9pZCIsIHRvdGFsX3RpbHR5cGVzKSkgCm5hX3NhbXBsZXMgPC0gaWhjX3RhYmxlX3N1YnNldCAlPiUgc3Vic2V0KHNlbGVjdD0tYyhjb25kZW5zZWRfaWQsIHBhdGllbnRfaWQpKSAlPiUgYXBwbHkoMSwgZnVuY3Rpb24oeCkgYW55KGlzLm5hKHgpKSkKaWhjX3RhYmxlX3N1YnNldCA8LSBpaGNfdGFibGVfc3Vic2V0WyFuYV9zYW1wbGVzLF0KCmloY190YWJsZV9tZWx0ZWQgPC0gbWVsdChpaGNfdGFibGVfc3Vic2V0LCBpZC52YXJzID0gYygiY29uZGVuc2VkX2lkIiwgInBhdGllbnRfaWQiKSwgbWVhc3VyZS52YXJzID0gdG90YWxfdGlsdHlwZXMsIHZhcmlhYmxlLm5hbWUgPSAidGlsdHlwZSIsIHZhbHVlLm5hbWUgPSAiZGVuc2l0eSIpCmloY190YWJsZV9tZWx0ZWQkY2VsbHR5cGUgPC0gaWZlbHNlKGloY190YWJsZV9tZWx0ZWQkdGlsdHlwZSAlaW4lIGMoIlRfQ0Q4X2RlbnNpdHkiLCAiVF9DRDRfZGVuc2l0eSIpLCB5ZXMgPSAiVF9jZWxsIiwgbm8gPSAiQl9jZWxsIikKCmlkc19pbnRlcnNlY3QgPC0gaW50ZXJzZWN0KGloY190YWJsZV9tZWx0ZWQkY29uZGVuc2VkX2lkLCB0Y3JfbWF0X21lbHQkY29uZGVuc2VkX2lkKQppaGNfdGFibGVfbWVsdGVkIDwtIHN1YnNldChpaGNfdGFibGVfbWVsdGVkLCBjb25kZW5zZWRfaWQgJWluJSBpZHNfaW50ZXJzZWN0KQp0Y3JfbWF0X21lbHQgPC0gc3Vic2V0KHRjcl9tYXRfbWVsdCwgY29uZGVuc2VkX2lkICVpbiUgaWRzX2ludGVyc2VjdCkKYmNyX21hdF9tZWx0IDwtIHN1YnNldChiY3JfbWF0X21lbHQsIGNvbmRlbnNlZF9pZCAlaW4lIGlkc19pbnRlcnNlY3QpCgppaGNfY2VsbHR5cGVfdG90YWxzIDwtIGloY190YWJsZV9tZWx0ZWQgJT4lIGdyb3VwX2J5KGNvbmRlbnNlZF9pZCkgJT4lIHN1bW1hcmlzZSh0X3RvdGFsPXN1bShkZW5zaXR5W2NlbGx0eXBlID09ICJUX2NlbGwiXSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJfdG90YWw9c3VtKGRlbnNpdHlbY2VsbHR5cGUgPT0gIkJfY2VsbCJdKSkKdF9zYW1wbGVfb3JkZXIgPC0gd2l0aChpaGNfY2VsbHR5cGVfdG90YWxzLCBjb25kZW5zZWRfaWRbb3JkZXIodF90b3RhbCwgZGVjcmVhc2luZyA9IEZBTFNFKV0pCmJfc2FtcGxlX29yZGVyIDwtIHdpdGgoaWhjX2NlbGx0eXBlX3RvdGFscywgY29uZGVuc2VkX2lkW29yZGVyKGJfdG90YWwsIGRlY3JlYXNpbmcgPSBGQUxTRSldKQoKcGxvdF9zdGFja2VkX3hjciA8LSBmdW5jdGlvbih4Y3JfbWF0X21lbHQsIHNhbXBsZV9vcmRlciwgbGFiZWxfeCA9IEZBTFNFKSB7CiAgeGNyX21hdF9tZWx0JGNvbmRlbnNlZF9pZCA8LSBmYWN0b3IoeGNyX21hdF9tZWx0JGNvbmRlbnNlZF9pZCwgbGV2ZWxzID0gc2FtcGxlX29yZGVyKQogIHAgPC0gZ2dwbG90KHhjcl9tYXRfbWVsdCwgYWVzKHg9Y29uZGVuc2VkX2lkLCB5ID0gcHJvcG9ydGlvbikpICsgZ2VvbV9iYXIoYWVzKGZpbGw9Zm9yY2F0czo6ZmN0X3JldihyYW5rKSksIHN0YXQgPSAiaWRlbnRpdHkiKSArIHRoZW1lX2J3KCkgKyBpdGhpLnV0aWxzOjp0aGVtZV9QdWJsaWNhdGlvbigpICsgaXRoaS51dGlsczo6dGhlbWVfbmF0dXJlKCkgKyBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBYQ1JfUkFOS19DT0xTKSArIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBjKDAsMCkpICsgZ3VpZGVzKGZpbGw9RkFMU0UpICsgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCkpICsgaXRoaS51dGlsczo6Z2dtYXJnaW5zKHR5cGUgPSAidG9wbWludXMiKQogIGlmICghbGFiZWxfeCkgewogICAgcmV0dXJuKHAgKyB4bGFiKCIiKSkKICB9IGVsc2UgewogICAgcmV0dXJuKHApCiAgfQp9CgpwMSA8LSBwbG90X3N0YWNrZWRfeGNyKHRjcl9tYXRfbWVsdCwgc2FtcGxlX29yZGVyID0gdF9zYW1wbGVfb3JkZXIsIGxhYmVsX3ggPSBGQUxTRSkKcDIgPC0gcGxvdF9zdGFja2VkX3hjcihiY3JfbWF0X21lbHQsIHNhbXBsZV9vcmRlciA9IGJfc2FtcGxlX29yZGVyLCBsYWJlbF94ID0gRkFMU0UpCgoKY2VsbHR5cGVzIDwtIGMoIlRfY2VsbCIsIAogICAgICAgICAgICAgICAiQl9jZWxsIikKCmNyZWF0ZV90aWxwbG90cyA8LSBmdW5jdGlvbihpaGNfdGFibGVfbWVsdGVkLCBzYW1wbGVfb3JkZXIsIHNlbGVjdGVkX2NlbGx0eXBlKSB7CiAgZGF0IDwtIHN1YnNldChpaGNfdGFibGVfbWVsdGVkLCBjZWxsdHlwZSA9PSBzZWxlY3RlZF9jZWxsdHlwZSkKICBkYXQkY29uZGVuc2VkX2lkIDwtIGZhY3RvcihkYXQkY29uZGVuc2VkX2lkLCBsZXZlbHMgPSBzYW1wbGVfb3JkZXIpCiAgZGF0JHBhdGllbnRfaWQgPC0gaXRoaS5tZXRhOjpmYWN0b3JfaWQoZGF0JHBhdGllbnRfaWQsIHR5cGUgPSAicGF0aWVudF9pZCIsIGRiX3BhdGgpCiAgZGF0IDwtIGRhdFtvcmRlcihkYXQkY29uZGVuc2VkX2lkKSxdCiAgc3VidGlscyA8LSBkYXQkdGlsdHlwZSAlPiUgdW5pcXVlCiAgc3VicGxvdHMgPC0gbGFwcGx5KHN1YnRpbHMsIGZ1bmN0aW9uKHkpIHsKICAgIHAgPC0gZ2dwbG90KGRhdCAlPiUgc3Vic2V0KHRpbHR5cGUgPT0geSksIGFlcyh4PWNvbmRlbnNlZF9pZCwgeSA9IGRlbnNpdHkpKSArIGdlb21fYmFyKGFlcyhmaWxsPXBhdGllbnRfaWQpLCBzdGF0ID0gImlkZW50aXR5IikgKyB0aGVtZV9idygpICsgdGhlbWVfUHVibGljYXRpb24oKSArIHRoZW1lX25hdHVyZSgpICsgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwwKSkgKyBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBhbm5vdGF0aW9uX2NvbG91cnMkcGF0aWVudF9pZCkgKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSkgKyB4bGFiKCIiKSArIHlsYWIoeSkgKyBndWlkZXMoZmlsbD1GQUxTRSkgKyBpdGhpLnV0aWxzOjpnZ21hcmdpbnModHlwZSA9ICJ0b3BtaW51cyIpCiAgICByZXR1cm4ocCkKICB9KQogIG5hbWVzKHN1YnBsb3RzKSA8LSBzdWJ0aWxzCiAgcmV0dXJuKHN1YnBsb3RzKQp9Cgp0X3RpbHBsb3RzIDwtIGNyZWF0ZV90aWxwbG90cyhpaGNfdGFibGVfbWVsdGVkLCBzYW1wbGVfb3JkZXIgPSB0X3NhbXBsZV9vcmRlciwgc2VsZWN0ZWRfY2VsbHR5cGUgPSAiVF9jZWxsIikKYl90aWxwbG90cyA8LSBjcmVhdGVfdGlscGxvdHMoaWhjX3RhYmxlX21lbHRlZCwgc2FtcGxlX29yZGVyID0gYl9zYW1wbGVfb3JkZXIsIHNlbGVjdGVkX2NlbGx0eXBlID0gIkJfY2VsbCIpCnRpbHBsb3RzIDwtIGxpc3QoVF9jZWxsPXRfdGlscGxvdHMsIEJfY2VsbD1iX3RpbHBsb3RzKQoKdGNyX2RpdmVyc2l0eV90YWJsZSA8LSB0Y3JfZGl2ZXJzaXR5ICU+JSBwbHlyOjpyZW5hbWUoYygnb2JzZXJ2ZWREaXZlcnNpdHlfbWVhbic9J1VuaXF1ZSBjbG9ub3R5cGVzJywgJ3NoYW5ub25XaWVuZXJJbmRleF9tZWFuJz0nRW50cm9weScpKQpiY3JfZGl2ZXJzaXR5X3RhYmxlIDwtIGJjcl9kaXZlcnNpdHkgJT4lIHBseXI6OnJlbmFtZShjKCdvYnNlcnZlZERpdmVyc2l0eV9tZWFuJz0nVW5pcXVlIGNsb25vdHlwZXMnLCAnc2hhbm5vbldpZW5lckluZGV4X21lYW4nPSdFbnRyb3B5JykpCgpwbG90X3hjcl9kaXZlcnNpdHkgPC0gZnVuY3Rpb24oeGNyX2RpdmVyc2l0eV90YWJsZSwgc2FtcGxlX29yZGVyLCBkaXZlcnNpdHlfbWVhc3VyZXMgPSBjKCJVbmlxdWUgY2xvbm90eXBlcyIsICJFbnRyb3B5IikpIHsKICB4Y3JfZGl2ZXJzaXR5X3RhYmxlIDwtIHN1YnNldCh4Y3JfZGl2ZXJzaXR5X3RhYmxlLCBjb25kZW5zZWRfaWQgJWluJSBzYW1wbGVfb3JkZXIpCiAgZGl2ZXJzaXR5X3RhYmxlX21lbHRlZCA8LSBtZWx0KHhjcl9kaXZlcnNpdHlfdGFibGUsIGlkLnZhcnMgPSBjKCJjb25kZW5zZWRfaWQiLCAicGF0aWVudF9pZCIpLCBtZWFzdXJlLnZhcnMgPSBkaXZlcnNpdHlfbWVhc3VyZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhcmlhYmxlLm5hbWUgPSAibWVhc3VyZSIsIHZhbHVlLm5hbWUgPSAiZGl2ZXJzaXR5IikKICBkaXZlcnNpdHlfdGFibGVfbWVsdGVkJGNvbmRlbnNlZF9pZCA8LSBmYWN0b3IoZGl2ZXJzaXR5X3RhYmxlX21lbHRlZCRjb25kZW5zZWRfaWQsIGxldmVscyA9IHNhbXBsZV9vcmRlcikKICBzdWJwbG90cyA8LSBsYXBwbHkoZGl2ZXJzaXR5X21lYXN1cmVzLCBmdW5jdGlvbihkaXZ0eXBlKSB7CiAgICBkYXQgPC0gc3Vic2V0KGRpdmVyc2l0eV90YWJsZV9tZWx0ZWQsIG1lYXN1cmUgPT0gZGl2dHlwZSkKICAgIGRhdCRwYXRpZW50X2lkIDwtIGl0aGkubWV0YTo6ZmFjdG9yX2lkKGRhdCRwYXRpZW50X2lkLCB0eXBlID0gInBhdGllbnRfaWQiLCBkYl9wYXRoKQogICAgCiAgICBnZ3Bsb3QoZGF0LCBhZXMoeD1jb25kZW5zZWRfaWQsIHkgPSBkaXZlcnNpdHkpKSArIGdlb21fYmFyKGFlcyhmaWxsPXBhdGllbnRfaWQpLCBzdGF0ID0gImlkZW50aXR5IikgKyB0aGVtZV9idygpICsgdGhlbWVfUHVibGljYXRpb24oKSArIHRoZW1lX25hdHVyZSgpICsgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwwKSkgKyBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBhbm5vdGF0aW9uX2NvbG91cnMkcGF0aWVudF9pZCkgKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSkgKyB4bGFiKCIiKSArIHlsYWIoZGl2dHlwZSkgKyBndWlkZXMoZmlsbD1GQUxTRSkgKyBpdGhpLnV0aWxzOjpnZ21hcmdpbnModHlwZSA9ICJ0b3BtaW51cyIpCiAgfSkKICBuYW1lcyhzdWJwbG90cykgPC0gZGl2ZXJzaXR5X21lYXN1cmVzCiAgcmV0dXJuKHN1YnBsb3RzKQp9Cgp0Y3JkaXZwbG90cyA8LSBwbG90X3hjcl9kaXZlcnNpdHkodGNyX2RpdmVyc2l0eV90YWJsZSwgc2FtcGxlX29yZGVyID0gdF9zYW1wbGVfb3JkZXIpCmJjcmRpdnBsb3RzIDwtIHBsb3RfeGNyX2RpdmVyc2l0eShiY3JfZGl2ZXJzaXR5X3RhYmxlLCBzYW1wbGVfb3JkZXIgPSBiX3NhbXBsZV9vcmRlcikKCmNvbnN0cnVjdF9jb21iaW5lZF9wbG90IDwtIGZ1bmN0aW9uKGNlbGx0eXBlKSB7CiAgaWYgKGNlbGx0eXBlID09ICJUX2NlbGwiKSB7CiAgICByYXdfZ3Bsb3RzIDwtIGxpc3QoCiAgICAgIHRpbHBsb3RzJFRfY2VsbCRUX0NEOF9kZW5zaXR5LAogICAgICB0aWxwbG90cyRUX2NlbGwkVF9DRDRfZGVuc2l0eSwKICAgICAgcDEsCiAgICAgIHRjcmRpdnBsb3RzJGBVbmlxdWUgY2xvbm90eXBlc2AsCiAgICAgIHRjcmRpdnBsb3RzJEVudHJvcHkKICAgICkKICB9IGVsc2UgaWYgKGNlbGx0eXBlID09ICJCX2NlbGwiKSB7CiAgICByYXdfZ3Bsb3RzIDwtIGxpc3QoCiAgICAgIHRpbHBsb3RzJEJfY2VsbCRUX0NEMjBfZGVuc2l0eSwKICAgICAgdGlscGxvdHMkQl9jZWxsJFRfUGxhc21hX2RlbnNpdHksCiAgICAgIHAyLAogICAgICBiY3JkaXZwbG90cyRgVW5pcXVlIGNsb25vdHlwZXNgLAogICAgICBiY3JkaXZwbG90cyRFbnRyb3B5CiAgICApCiAgfQogIAogIGdyb2JfZ3Bsb3RzIDwtIGxhcHBseShyYXdfZ3Bsb3RzLCBmdW5jdGlvbih4KSBnZ3Bsb3RHcm9iKHgpKQogIGNvbWJpbmVkX2dyb2IgPC0gZG8uY2FsbChncmlkRXh0cmE6OnJiaW5kLmd0YWJsZSwgZ3JvYl9ncGxvdHMpCiAgcmV0dXJuKGNvbWJpbmVkX2dyb2IpCn0KCnRfY29tYmluZWRfcGxvdCA8LSBjb25zdHJ1Y3RfY29tYmluZWRfcGxvdChjZWxsdHlwZSA9ICJUX2NlbGwiKQpiX2NvbWJpbmVkX3Bsb3QgPC0gY29uc3RydWN0X2NvbWJpbmVkX3Bsb3QoY2VsbHR5cGUgPSAiQl9jZWxsIikKYGBgCgojIyMgVENSCgpgYGB7cn0KZ3JpZC5uZXdwYWdlKCkKZ3JpZC5kcmF3KHRfY29tYmluZWRfcGxvdCkKYGBgCgpJbiBnZW5lcmFsLCB3ZSBjYW4gc2VlIHRoYXQgaGlnaGVyIENEOC9DRDQgVCBjZWxsIGRlbnNpdHkgaXMgYWNjb21wYW5pZWQgYnkgYW4gaW5jcmVhc2UgaW4gdGhlIGZyYWN0aW9uIG9mIGNsb25vdHlwZSByZWFkcyBhY2NvdW50ZWQgZm9yIGJ5IHJhcmUgY2xvbm90eXBlcy4gSW4gc29tZSBzYW1wbGVzL3BhdGllbnRzIChlLmcuIHBhdGllbnQgMTUpIHRoZXJlIGFwcGVhcnMgdG8gYmUgJ2V4cGFuc2lvbicgb2YgdGhlIG1vc3QgZG9taW5hdCBjbG9ub3R5cGUocyksIGJ1dCBpZiB5b3UgbG9vayBhdCB0aGUgcGF0aWVudCAxNSBzYW1wbGUgd2l0aCBsb3cgVElMIGRlbnNpdHkgKGluIHRoZSBsZWZ0IGhhbGYgb2YgdGhlIHBsb3QsIG5lYXIgdGhlIG1pZGRsZSkgaXQgYmVjb21lcyBhcHBhcmVudCB0aGF0IHNhbXBsZXMgd2l0aCBsb3dlciBUIGNlbGwgZGVuc2l0eSBoYXZlIGEgaGlnaGVyIHByb3BvcnRpb24gb2YgY2xvbm90eXBlcyBtYWRlIHVwIGJ5IHJhcmUgY2xvbm90eXBlcy4gCgpUaGVyZWZvcmUsIGl0IHdvdWxkIGJlIGNvcnJlY3QgdG8gc2F5IHRoYXQgdGhlIFRJTCByZXNwb25zZXMgaW4gaGlnaC1USUwgZGVuc2l0eSBzYW1wbGVzIGFyZSBwb2x5Y2xvbmFsLCByYXRoZXIgdGhhbiBkb21pbmF0ZWQgYnkgYSBzaW5nbGUgY2xvbmUuIAoKQXMgeW91IGNhbiBzZWUgZnJvbSB0aGUgbGFzdCB0d28gYmFycGxvdHMsIG91ciBkaXZlcnNpdHkgbWVhc3VyZXMgYXJlIGNhcHR1cmluZyB0aGUgb2JzZXJ2YXRpb24gdGhhdCBsb3dlciBwcm9wb3J0aW9uIGFjY291bnRlZCBmb3IgYnkgdG9wIDEwIGNsb25vdHlwZXMgPSBoaWdoZXIgVENSIGRpdmVyc2l0eS4gIAoKCiMjIyBCQ1IKCmBgYHtyfQpncmlkLm5ld3BhZ2UoKQpncmlkLmRyYXcoYl9jb21iaW5lZF9wbG90KQpgYGAKClRoZSBzYW1lIGlzbid0IGFzIHRydWUgZm9yIEJDUnMgLS0gd2UgY2FuIHNlZSBldmlkZW5jZSB0aGF0IHNhbXBsZXMgd2l0aCB0aGUgaGlnaGVzdCBUSUwgZGVuc2l0aWVzIGFyZSBub3QgbmVjZXNzYXJpbHkgcG9seWNsb25hbC4gVGhpcyBpcyBpbiBhZ3JlZW1lbnQgd2l0aCBvdXIgb2JzZXJ2YXRpb24gdGhhdCBCQ1IgY2xvbmFsaXR5IGRvZXMgbm90IGNvcnJlbGF0ZSB3aXRoIEItY2VsbCBkZW5zaXRpZXMuIAoKVE9ETzogSURFQUxMWSBTSE9XIFVOQ0VSVEFJTlRZIElOIFhDUiBCQVJTIFRPTyAodXNpbmcgdGhlIGVzdGltYXRlcyBmb3IgVENSL0JDUikuCgpUT0RPOiBSZWxhYmVsIGF4aXMgbmFtZXMsIGFkZCBjb2xvdXIgbGVnZW5kIGZvciBwYXRpZW50cy4gRGlzY3VzcyBpZiBiZXN0IHdheSB0byBzaG93IHBhdGllbnRzIGlzIHdpdGggY29sb3VyZWQgYmFycyBvciB3aXRoIGEgc2VwYXJhdGUgYW5ub3RhdGlvbiB0cmFjay4gCgoKQXMgeW91IGNhbiBzZWUsIGl0J3MgcHJldHR5IG9idmlvdXMgdGhhdCB0aGUgZGl2ZXJzaXR5IG1lYXN1cmVzIGFyZSBkb2luZyBhIGdvb2Qgam9iIGNhcHR1cmluZyB0aGUgZGlzdHJpYnV0aW9uIG9mIHRvcCBjbG9ub3R5cGVzLiAKCg==