library(ithi.utils)
load_base_libs()
library(nestedRanksTest)

library(ithi.meta)
library(ithi.xcr)
library(ithi.ihc)
library(ithi.expr)
library(ithi.clones)
library(nanotools)

Colour palettes

pal_patient <- select_palette("patient")

Parameters

db_path <- snakemake@params$db

molecular_subtype_file <- snakemake@input$molecular_subtypes

ihc_table_path <- snakemake@input$ihc_table
tiltypes <- unlist(snakemake@params$tiltypes)

tcr_diversity_file <- snakemake@input$tcr_diversity
bcr_diversity_file <- snakemake@input$bcr_diversity

xcr_table_path <- snakemake@input$xcr_table

nanostring_data_path <- snakemake@input$nanostring_data
nanostring_annotations_path <- snakemake@input$nanostring_annotations

clone_tree_file <- snakemake@input$clone_tree_file
clone_prevalence_file <- snakemake@input$clone_prevalence_file
clone_branch_length_file <- snakemake@input$clone_branch_length_file

Metadata

db <- src_sqlite(db_path, create = FALSE)
samples <- collect(tbl(db, "samples"))

Molecular subtype classification

molsubtypes <- fread(molecular_subtype_file)
molsubtypes <- setNames(molsubtypes, c("condensed_id", "subtype"))

Counts

table(molsubtypes$subtype)

C1 C2 C4 C5 
24 36 15 41 

IHC

ihc_table <- fread(ihc_table_path)
ihc_table$patient_id <- factor(ihc_table$patient_id)

ihcm <- melt(ihc_table, id.vars = c("condensed_id", "patient_id"), measure.vars = tiltypes, 
    variable.name = "tiltype", value.name = "density")

ihcm_subtype <- merge(molsubtypes, ihcm, by = c("condensed_id"))
stats <- setNames(ddply(ihcm_subtype, .(tiltype), function(x) {
    res <- kruskal.test(density ~ factor(subtype), data = x)
    pvalue <- res$p.value
    eq <- substitute(italic(P) == p, list(p = format(pvalue, digits = 3)))
    return(as.character(as.expression(eq)))
}), c("tiltype", "p.value"))

ggplot(ihcm_subtype, aes(x = subtype, y = density)) + geom_boxplot() + geom_jitter(position = position_jitter(width = 0.2, 
    height = 0), aes(colour = patient_id)) + theme_bw() + theme_Publication() + 
    scale_color_manual(values = pal_patient) + facet_wrap(~tiltype, scales = "free", 
    nrow = 2) + geom_text(data = stats, aes(x = Inf, y = Inf, label = p.value), 
    hjust = 1.1, vjust = 1.5, size = 3, parse = TRUE)

TCR/BCR diversity

diversity_files <- list(tcr = tcr_diversity_file, bcr = bcr_diversity_file)

diversity <- lapply(diversity_files, function(f) {
    dat <- read_xcr_diversity_file(f, db_path)
    merge(dat, molsubtypes, by = c("condensed_id"))
})

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

Read 49.2% of 304822 rows
Read 85.3% of 304822 rows
Read 88.6% of 304822 rows
Read 304822 rows and 18 (of 18) columns from 0.070 GB file in 00:00:05
xcr_table$condensed_id <- as.character(xcr_table$condensed_id)
patients <- unique(xcr_table$patient_id)

tcr_segment_type <- "TRB"
bcr_segment_type <- "IGH"
id_type <- "condensed_id"

# not actually beta diversity but still fun
betas <- rbind.fill(lapply(patients, function(patient) {
    tcr_clonotypes <- subset(xcr_table, type == tcr_segment_type & patient_id == 
        patient)
    bcr_clonotypes <- subset(xcr_table, type == bcr_segment_type & patient_id == 
        patient)
    tcr_cross_table <- cross_tabulate(tcr_clonotypes, id_type = id_type)
    bcr_cross_table <- cross_tabulate(bcr_clonotypes, id_type = id_type)
    
    cross_tables <- list(tcr = tcr_cross_table, bcr = bcr_cross_table)
    
    betavals <- rbind.fill(lapply(seq_along(cross_tables), function(i) {
        cross_table <- cross_tables[[i]]
        df <- cbind(type = names(cross_tables)[i], compute_beta_diversity(cross_table, 
            mode = "union", measure = "count"))
        return(df)
    }))
    return(cbind(patient_id = patient, betavals))
}))
betas_list <- split(betas, f = betas$type)

compute_diversity_index <- function(xcr_table, type = "rareness", rare_threshold = 1e-04) {
    if (type == "rareness") {
        PREVALENCE_TYPE <- "freq"
        
        res <- xcr_table %>% group_by_(.dots = c("condensed_id", "patient_id", 
            "type")) %>% summarise_(rareness = lazyeval::interp(~sum(var[var < 
            rare_threshold]), var = as.name(PREVALENCE_TYPE)))
        res <- split(res, f = res$type)
    } else if (type == "berger_parker") {
        res <- xcr_table %>% group_by_(.dots = c("condensed_id", "patient_id", 
            "type")) %>% summarise(berger_parker = 1/max(freq))
        res <- split(res, f = res$type)
    }
    return(res)
}

cdf_clonotype_freqs <- function(xcr_table, segment = "TRB") {
    ggplot(xcr_table, aes(x = freq)) + stat_ecdf(geom = "step") + scale_x_log10() + 
        facet_wrap(~condensed_id, ncol = 5)
}

plot_diversity_subtype <- function(diversity, col) {
    ignore <- lapply(diversity, function(df) {
        p <- ggplot(df, aes_string(x = "subtype", y = col)) + geom_boxplot() + 
            geom_jitter(position = position_jitter(width = 0.2, height = 0), 
                aes(colour = patient_id)) + theme_bw() + theme_Publication() + 
            scale_color_manual(values = pal_patient) + xlab("Subtype") + ylab("Diversity")
        print(p)
    })
}

Richness

plot_diversity_subtype(diversity, "observedDiversity_mean")

Shannon entropy

plot_diversity_subtype(diversity, "shannonWienerIndex_mean")

Divergence

plot_diversity_subtype(diversity, "inverseSimpsonIndex_mean")

D50 index

plot_diversity_subtype(diversity, "d50Index_mean")

Rareness

rareness <- compute_diversity_index(xcr_table, type = "rareness", rare_threshold = 1e-04)
rareness <- lapply(rareness, function(x) {
    x$patient_id <- as.character(x$patient_id)
    merge(x, molsubtypes, by = c("condensed_id"))
})
plot_diversity_subtype(rareness, "rareness")

Berger-Parker

dominance <- compute_diversity_index(xcr_table, type = "berger_parker")
dominance <- lapply(dominance, function(x) {
    x$patient_id <- as.character(x$patient_id)
    merge(x, molsubtypes, by = c("condensed_id"))
})
plot_diversity_subtype(dominance, "berger_parker")

Sample-specific clonotype proportion

betas_list <- lapply(betas_list, function(x) {
    x$patient_id <- as.character(x$patient_id)
    merge(x, molsubtypes, by = c("condensed_id"))
})
plot_diversity_subtype(betas_list, "prop_unique")

CDFs

cdf_clonotype_freqs(xcr_table, "TRB")

cdf_clonotype_freqs(xcr_table, "IGH")

Nanostring

exprs <- fread(nanostring_data_path)
labels <- fread(nanostring_annotations_path)

celltype_matrix <- rownames_to_column(as.data.frame(t(create_celltype_matrix(exprs, 
    labels, db_path))), var = "condensed_id")
celltype_vars <- colnames(celltype_matrix)[!colnames(celltype_matrix) == "condensed_id"]

celltype_matrix_melted <- melt(celltype_matrix, id.vars = c("condensed_id"), 
    measure.vars = celltype_vars, variable.name = "type", value.name = "expr")

celltype_subtype <- merge(celltype_matrix_melted, molsubtypes, by = c("condensed_id"))
celltype_subtype$patient_id <- factor(map_id(celltype_subtype$condensed_id, 
    from = "condensed_id", to = "patient_id", db_path))
stats <- setNames(ddply(celltype_subtype, .(type), function(x) {
    res <- kruskal.test(expr ~ factor(subtype), data = x)
    pvalue <- res$p.value
    eq <- substitute(italic(P) == p, list(p = format(pvalue, digits = 3)))
    return(as.character(as.expression(eq)))
}), c("type", "p.value"))

ggplot(celltype_subtype, aes(x = subtype, y = expr)) + geom_boxplot() + geom_jitter(position = position_jitter(width = 0.2, 
    height = 0), aes(colour = patient_id)) + theme_bw() + theme_Publication() + 
    scale_color_manual(values = pal_patient) + facet_wrap(~type, scales = "free", 
    nrow = 4) + geom_text(data = stats, aes(x = Inf, y = Inf, label = p.value), 
    hjust = 1.1, vjust = 1.5, size = 3, parse = TRUE)

pathway_matrix <- rownames_to_column(as.data.frame(t(create_pathway_matrix(exprs, 
    labels, db_path))), var = "condensed_id")
pathway_vars <- colnames(pathway_matrix)[!colnames(pathway_matrix) == "condensed_id"]

pathway_matrix_melted <- melt(pathway_matrix, id.vars = c("condensed_id"), measure.vars = pathway_vars, 
    variable.name = "type", value.name = "expr")

pathway_subtype <- merge(pathway_matrix_melted, molsubtypes, by = c("condensed_id"))
pathway_subtype$patient_id <- factor(map_id(pathway_subtype$condensed_id, from = "condensed_id", 
    to = "patient_id", db_path))
stats <- setNames(ddply(pathway_subtype, .(type), function(x) {
    res <- kruskal.test(expr ~ factor(subtype), data = x)
    pvalue <- res$p.value
    eq <- substitute(italic(P) == p, list(p = format(pvalue, digits = 3)))
    return(as.character(as.expression(eq)))
}), c("type", "p.value"))

ggplot(pathway_subtype, aes(x = subtype, y = expr)) + geom_boxplot() + geom_jitter(position = position_jitter(width = 0.2, 
    height = 0), aes(colour = patient_id)) + theme_bw() + theme_Publication() + 
    scale_color_manual(values = pal_patient) + facet_wrap(~type, scales = "free", 
    nrow = 4) + geom_text(data = stats, aes(x = Inf, y = Inf, label = p.value), 
    hjust = 1.1, vjust = 1.5, size = 3, parse = TRUE)

Relation to clonal composition

Here we’ll relate intrapatient differences in molecular subtype to differences in clonal composition. Questions: * Are tumours with different molecular subtypes more clonally different?

tree_branch_data <- read_clone_tree_data(clone_tree_file, clone_branch_length_file, 
    clone_prevalence_file, db_path)
trees <- lapply(tree_branch_data, function(x) x$tree)
prevalences <- rbind.fill(lapply(tree_branch_data, function(x) x$prevalence_dat))
branch_lengths <- rbind.fill(lapply(tree_branch_data, function(x) x$branch_dat))

ccfs <- compute_ccf(prevalences, trees, id_type = "condensed_id")
ccfs_labeled <- merge(ccfs, branch_lengths, by = c("label", "node", "patient_id"))

## Need non-normalized distances
clone_dists <- clone_distances(ccfs_labeled, normalize = TRUE, id_type = "condensed_id")

clone_dists$patient_id <- factor(clone_dists$patient_id)

clone_dists_melted <- rbind.fill(lapply(1:nrow(clone_dists), function(i) {
    patient <- clone_dists$patient_id[i]
    distmat <- clone_dists$dist_clones_weighted[[i]]
    
    distdf <- setNames(melt(as.matrix(distmat)), c("id1", "id2", "clone_dist"))
    distdf$id1 <- factor(distdf$id1)
    distdf$id2 <- factor(distdf$id2)
    distdf <- subset(distdf, as.numeric(id1) > as.numeric(id2))
    return(distdf)
}))
df <- merge(clone_dists_melted, setNames(molsubtypes, c("id1", "subtype1")))
df <- merge(df, setNames(molsubtypes, c("id2", "subtype2")))
df$equal_subtypes <- df$subtype1 == df$subtype2
df$patient_id <- map_id(df$id1, from = "condensed_id", to = "patient_id", db_path)
ggplot(df, aes(x = equal_subtypes, y = clone_dist)) + geom_boxplot(outlier.size = -1) + 
    geom_jitter(aes(colour = patient_id), position = position_jitter(width = 0.2, 
        height = 0)) + theme_bw() + theme_Publication() + scale_colour_manual(values = pal_patient)

Making this comparison across patients is unfair, as clone distance values (even when normalized) may be very different in scale from patient to patient. It’s obvious there’s no pattern ACROSS patients, but how about WITHIN patients?

If we assume a Gaussian response variable (which is definitely not true):

summary(lmer(clone_dist ~ equal_subtypes + (1 | patient_id), df))
Linear mixed model fit by REML t-tests use Satterthwaite approximations
  to degrees of freedom [lmerMod]
Formula: clone_dist ~ equal_subtypes + (1 | patient_id)
   Data: df

REML criterion at convergence: -244.6

Scaled residuals: 
    Min      1Q  Median      3Q     Max 
-3.4180 -0.4943 -0.0424  0.5238  2.3332 

Random effects:
 Groups     Name        Variance Std.Dev.
 patient_id (Intercept) 0.02447  0.1564  
 Residual               0.01821  0.1349  
Number of obs: 252, groups:  patient_id, 14

Fixed effects:
                     Estimate Std. Error         df t value Pr(>|t|)    
(Intercept)          0.250493   0.044280  14.090000   5.657 5.77e-05 ***
equal_subtypesTRUE  -0.005325   0.020301 242.570000  -0.262    0.793    
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Correlation of Fixed Effects:
            (Intr)
eql_sbtTRUE -0.221

There’s a trend toward lower clone distance between pairs of tumours with equal subtype, but this is not significant.

What if we use a nonparametric test?

tmp <- df %>% group_by(patient_id) %>% summarise(both_levels = !Reduce("&", 
    equal_subtypes) & Reduce("|", equal_subtypes))

df_filtered <- subset(df, patient_id %in% subset(tmp, both_levels)$patient_id)

res <- nestedRanksTest(clone_dist ~ !equal_subtypes | patient_id, data = df_filtered, 
    n.iter = 5000)
res

    Nested Ranks Test

data:  clone_dist by !equal_subtypes grouped by patient_id
Z = 0.00081235, p-value = 0.4866
alternative hypothesis: Z lies above bootstrapped null values
null values:
      0%       1%       5%      10%      25%      50%      75%      90% 
-0.36149 -0.25509 -0.17872 -0.14054 -0.07392 -0.00244  0.07311  0.14054 
     95%      99%     100% 
 0.17551  0.24696  0.34768 

bootstrap iterations: 5000 
group weights:
         10          11          12          13          14          15 
0.158407799 0.017059301 0.017059301 0.019496344 0.012997563 0.006498781 
         17           2           3           4           7           9 
0.019496344 0.004061738 0.604386677 0.089358245 0.043866775 0.007311129 
plot(res)

We do see a trend! We can only do this for patients with samples in both clusters. So, samples with different molecular subtypes are also more likely to have different clonal compositions. But this does not appear to be significant when I run it with the new subtyping (combined using ICGC for subtyping …).

LS0tCnRpdGxlOiAiTW9sZWN1bGFyIHN1YnR5cGVzIgpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHRydWUKICAgIHRvY19kZXB0aDogNQogICAgdG9jX2Zsb2F0OiB0cnVlCnBhcmFtczoKICBybWQ6ICJtb2xlY3VsYXJfc3VidHlwZXMuUm1kIgotLS0KICAgICAgICAgICAgICAgICAgICAgICAgYGBge3IsIGVjaG89RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CgojIyMjIyMjIyBTbmFrZW1ha2UgaGVhZGVyICMjIyMjIyMjCmxpYnJhcnkobWV0aG9kcykKU25ha2VtYWtlIDwtIHNldENsYXNzKAogICAgIlNuYWtlbWFrZSIsCiAgICBzbG90cyA9IGMoCiAgICAgICAgaW5wdXQgPSAibGlzdCIsCiAgICAgICAgb3V0cHV0ID0gImxpc3QiLAogICAgICAgIHBhcmFtcyA9ICJsaXN0IiwKICAgICAgICB3aWxkY2FyZHMgPSAibGlzdCIsCiAgICAgICAgdGhyZWFkcyA9ICJudW1lcmljIiwKICAgICAgICBsb2cgPSAibGlzdCIsCiAgICAgICAgcmVzb3VyY2VzID0gImxpc3QiLAogICAgICAgIGNvbmZpZyA9ICJsaXN0IiwKICAgICAgICBydWxlID0gImNoYXJhY3RlciIKICAgICkKKQpzbmFrZW1ha2UgPC0gU25ha2VtYWtlKAogICAgaW5wdXQgPSBsaXN0KCcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvYW5hbHlzaXMvUm1kL19zaXRlLnltbCcsICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy90YWJsZXMvcnVuMi9jbG9uZXMvdHJlZV9kYXRhLnRzdicsICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvZGF0YS9leHByZXNzaW9uL25hbm9zdHJpbmcvcGFuY2FuY2VyX2Fubm90YXRpb25zLnRzdicsICcvc2hhaGxhYi9hbHpoYW5nL3BpcGVsaW5lX291dHB1dHMvaXRoX2ltbXVuZS9taXhjci9taXhjcl9ydW5zL2l0aF8xXzJfMy9taXhjcjUvcG9zdHByb2Nlc3MvVFJCL3Bvc3RmaWx0ZXJfZGl2ZXJzaXR5X3N0YXRzL2RpdmVyc2l0eS5zdHJpY3QucmVzYW1wbGVkLnR4dCcsICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy90YWJsZXMvcnVuMi9paGNfdGFibGUudHN2JywgJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9yZXN1bHRzL25hbm9zdHJpbmdfcmVzdWx0cy9pdGhfZnVsbC9xYy9saW1tYV9xdWFudGlsZS9ub3JtYWxpemVkX2V4cHJlc3Npb25fdm9hX2xhYmVsc19maWx0ZXJlZC50c3YnLCAnL3NoYWhsYWIvYWx6aGFuZy9waXBlbGluZV9vdXRwdXRzL2l0aF9pbW11bmUvbWl4Y3IvbWl4Y3JfcnVucy9pdGhfMV8yXzMvbWl4Y3I1L3Bvc3Rwcm9jZXNzL0lHSC9wb3N0ZmlsdGVyX2RpdmVyc2l0eV9zdGF0cy9kaXZlcnNpdHkuc3RyaWN0LnJlc2FtcGxlZC50eHQnLCAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3BhcGVyL3Jlc3VsdHMvdGFibGVzL3J1bjIveGNyX3RhYmxlLnRzdicsICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy90YWJsZXMvcnVuMi9jbG9uZXMvY2xvbmVfZGF0YS50c3YnLCAnUm1kL21vbGVjdWxhcl9zdWJ0eXBlcy5SbWQnLCAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3BhcGVyL3Jlc3VsdHMvdGFibGVzL3J1bjIvY2xvbmVzL2JyYW5jaF9kYXRhLnRzdicsICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvZGF0YS9pdGgvY29tcGxldGUvY2xvbmFsX21lYXN1cmVzLnRzdicsICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy90YWJsZXMvcnVuMi9tb2xzdWJ0eXBlcy50c3YnLCAic2l0ZV9maWxlIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvYW5hbHlzaXMvUm1kL19zaXRlLnltbCcsICJjbG9uZV90cmVlX2ZpbGUiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9yZXN1bHRzL3RhYmxlcy9ydW4yL2Nsb25lcy90cmVlX2RhdGEudHN2JywgIm5hbm9zdHJpbmdfYW5ub3RhdGlvbnMiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9kYXRhL2V4cHJlc3Npb24vbmFub3N0cmluZy9wYW5jYW5jZXJfYW5ub3RhdGlvbnMudHN2JywgInRjcl9kaXZlcnNpdHkiID0gJy9zaGFobGFiL2FsemhhbmcvcGlwZWxpbmVfb3V0cHV0cy9pdGhfaW1tdW5lL21peGNyL21peGNyX3J1bnMvaXRoXzFfMl8zL21peGNyNS9wb3N0cHJvY2Vzcy9UUkIvcG9zdGZpbHRlcl9kaXZlcnNpdHlfc3RhdHMvZGl2ZXJzaXR5LnN0cmljdC5yZXNhbXBsZWQudHh0JywgImloY190YWJsZSIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3BhcGVyL3Jlc3VsdHMvdGFibGVzL3J1bjIvaWhjX3RhYmxlLnRzdicsICJuYW5vc3RyaW5nX2RhdGEiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9yZXN1bHRzL25hbm9zdHJpbmdfcmVzdWx0cy9pdGhfZnVsbC9xYy9saW1tYV9xdWFudGlsZS9ub3JtYWxpemVkX2V4cHJlc3Npb25fdm9hX2xhYmVsc19maWx0ZXJlZC50c3YnLCAiYmNyX2RpdmVyc2l0eSIgPSAnL3NoYWhsYWIvYWx6aGFuZy9waXBlbGluZV9vdXRwdXRzL2l0aF9pbW11bmUvbWl4Y3IvbWl4Y3JfcnVucy9pdGhfMV8yXzMvbWl4Y3I1L3Bvc3Rwcm9jZXNzL0lHSC9wb3N0ZmlsdGVyX2RpdmVyc2l0eV9zdGF0cy9kaXZlcnNpdHkuc3RyaWN0LnJlc2FtcGxlZC50eHQnLCAieGNyX3RhYmxlIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy90YWJsZXMvcnVuMi94Y3JfdGFibGUudHN2JywgImNsb25lX3ByZXZhbGVuY2VfZmlsZSIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3BhcGVyL3Jlc3VsdHMvdGFibGVzL3J1bjIvY2xvbmVzL2Nsb25lX2RhdGEudHN2JywgIm5vdGVib29rIiA9ICdSbWQvbW9sZWN1bGFyX3N1YnR5cGVzLlJtZCcsICJjbG9uZV9icmFuY2hfbGVuZ3RoX2ZpbGUiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9yZXN1bHRzL3RhYmxlcy9ydW4yL2Nsb25lcy9icmFuY2hfZGF0YS50c3YnLCAiaXRoX3N0YXRzIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvZGF0YS9pdGgvY29tcGxldGUvY2xvbmFsX21lYXN1cmVzLnRzdicsICJtb2xlY3VsYXJfc3VidHlwZXMiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9yZXN1bHRzL3RhYmxlcy9ydW4yL21vbHN1YnR5cGVzLnRzdicpLAogICAgb3V0cHV0ID0gbGlzdCgnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3BhcGVyL3Jlc3VsdHMvd2ViL21vbGVjdWxhcl9zdWJ0eXBlcy5uYi5odG1sJyksCiAgICBwYXJhbXMgPSBsaXN0KCcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvbWV0YWRhdGEvZGIvaW1tdW5lX3Byb2plY3Quc3FsaXRlMycsIGMoJ0VfQ0Q4X2RlbnNpdHknLCAnRV9DRDRfZGVuc2l0eScsICdFX0NEMjBfZGVuc2l0eScsICdFX1BsYXNtYV9kZW5zaXR5JywgJ1NfQ0Q4X2RlbnNpdHknLCAnU19DRDRfZGVuc2l0eScsICdTX0NEMjBfZGVuc2l0eScsICdTX1BsYXNtYV9kZW5zaXR5JyksICdpdGhpLWFuYWx5c2lzLW1vbGVjdWxhci1zdWJ0eXBlLWFuYWx5c2lzJywgInRpbHR5cGVzIiA9IGMoJ0VfQ0Q4X2RlbnNpdHknLCAnRV9DRDRfZGVuc2l0eScsICdFX0NEMjBfZGVuc2l0eScsICdFX1BsYXNtYV9kZW5zaXR5JywgJ1NfQ0Q4X2RlbnNpdHknLCAnU19DRDRfZGVuc2l0eScsICdTX0NEMjBfZGVuc2l0eScsICdTX1BsYXNtYV9kZW5zaXR5JyksICJkYiIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL21ldGFkYXRhL2RiL2ltbXVuZV9wcm9qZWN0LnNxbGl0ZTMnLCAibmFtZSIgPSAnaXRoaS1hbmFseXNpcy1tb2xlY3VsYXItc3VidHlwZS1hbmFseXNpcycpLAogICAgd2lsZGNhcmRzID0gbGlzdCgpLAogICAgdGhyZWFkcyA9IDEsCiAgICBsb2cgPSBsaXN0KCcvc2hhaGxhYi9hbHpoYW5nL2NsdXN0dG1wL3BhcGVyYW5hbHlzaXMyL21vbGVjdWxhcl9zdWJ0eXBlX2FuYWx5c2lzLmxvZycpLAogICAgcmVzb3VyY2VzID0gbGlzdCgpLAogICAgY29uZmlnID0gbGlzdCgieGNyX2Rpc3RhbmNlX21ldGhvZCIgPSAnaG9ybicsICJiY3JfZGl2ZXJzaXR5IiA9ICcvc2hhaGxhYi9hbHpoYW5nL3BpcGVsaW5lX291dHB1dHMvaXRoX2ltbXVuZS9taXhjci9taXhjcl9ydW5zL2l0aF8xXzJfMy9taXhjcjUvcG9zdHByb2Nlc3MvSUdIL3Bvc3RmaWx0ZXJfZGl2ZXJzaXR5X3N0YXRzL2RpdmVyc2l0eS5zdHJpY3QucmVzYW1wbGVkLnR4dCcsICJuZW9lZGl0aW5nX291dGRpciIgPSAnL3NoYWhsYWIvYWx6aGFuZy9waXBlbGluZV9vdXRwdXRzL2l0aF9pbW11bmUvbmVvZWRpdGluZy9ydW40JywgInRjcl9jbG9ub3R5cGVzIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3BpcGVsaW5lX291dHB1dHMvaXRoX2ltbXVuZS9taXhjci9taXhjcl9ydW5zL2l0aF8xXzJfMy9taXhjcjUvY2xvbm90eXBlcy9UUkJfY2xvbm90eXBlc19maWx0ZXJlZC50eHQnLCAidGlsc19mb3JfdmFyaWFiaWxpdHkiID0gYygnRV9DRDhfZGVuc2l0eScsICdFX0NENF9kZW5zaXR5JywgJ0VfQ0QyMF9kZW5zaXR5JywgJ0VfUGxhc21hX2RlbnNpdHknLCAnU19DRDhfZGVuc2l0eScsICdTX0NENF9kZW5zaXR5JywgJ1NfQ0QyMF9kZW5zaXR5JywgJ1NfUGxhc21hX2RlbnNpdHknKSwgImV4YW1wbGVfbXNhX3Bsb3QiID0gJy9zaGFobGFiL2FsemhhbmcvcGlwZWxpbmVfb3V0cHV0cy9pdGhfaW1tdW5lL2lncGFydGl0aW9uL3J1bjEzL29sZC9hbGlnbm1lbnRfcGxvdHMvbXNhL2l0aDJfMi9jbHVzdDkvaW5kZWxfcmV2ZXJzZWQuaHRtbCcsICJ4Y3JtYXBzY2FwZV90Y3JfcGF0aWVudF9vcmRlciIgPSBjKDE1LCAxLCAzLCA0LCAyLCAxNywgNywgMTQsIDksIDEwLCAxMiwgMTMsIDExLCAxNiksICJuYW5vc3RyaW5nX3NpZ25hdHVyZV9ub3RlYm9vayIgPSAnUm1kL25hbm9zdHJpbmdfc2lnbmF0dXJlcy5SbWQnLCAia25vd25fc3VidHlwZXNfbWVyZ2VkIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvZGF0YS9leHByZXNzaW9uL2tub3duX3N1YnR5cGVzX21lcmdlZC50c3YnLCAiY2xvbmVfcHJldmFsZW5jZV9maWxlIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvZGF0YS9pdGgvY29tcGxldGUvY2xvbmVfZGF0YS50c3YnLCAicGF0aWVudHNfZm9yX2Nsb25hbCIgPSBjKDEsIDIsIDMsIDQsIDcsIDksIDEwLCAxMSwgMTIsIDEzLCAxNCwgMTUsIDE2LCAxNyksICJtdXRzaWdfdGlsdHlwZXMiID0gYygnRV9DRDhfZGVuc2l0eScsICdFX0NENF9kZW5zaXR5JywgJ0VfQ0QyMF9kZW5zaXR5JywgJ0VfUGxhc21hX2RlbnNpdHknLCAnU19DRDhfZGVuc2l0eScsICdTX0NENF9kZW5zaXR5JywgJ1NfQ0QyMF9kZW5zaXR5JywgJ1NfUGxhc21hX2RlbnNpdHknLCAnVF9DRDhfZGVuc2l0eScsICdUX0NENF9kZW5zaXR5JywgJ1RfQ0QyMF9kZW5zaXR5JywgJ1RfUGxhc21hX2RlbnNpdHknKSwgIm1tY3RtX3NhbXBsZV9yZXN1bHRfZGlyIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcmVzdWx0cy9tbWN0bV9yZXN1bHRzL2l0aF9ieS1zYW1wbGUvb3V0cHV0JywgImZpZ3VyZV9nYWxsZXJ5X25vdGVib29rIiA9ICdSbWQvZmlndXJlcy5SbWQnLCAiaWdwYXJ0aXRpb25fb3V0ZGlyIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3BpcGVsaW5lX291dHB1dHMvaXRoX2ltbXVuZS9pZ3BhcnRpdGlvbi9ydW4yMicsICJpaGNfcnVuMSIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL2RhdGEvaWhjL2NkOGNkM2NkMjAvdmFsaWRhdGVkX3N0YXRzX3dlaWdodGVkX25ldy5yZGF0YScsICJzdWJ0eXBlX21hcmtlcl9maWxlIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvZGF0YS9leHByZXNzaW9uL25hbm9zdHJpbmcvc3VidHlwZV9tYXJrZXJzLnRzdicsICJ2YXJpYWJpbGl0eV90eXBlIiA9ICdzdGFiaWxpemUnLCAibmFub3N0cmluZ19hbm5vdGF0aW9ucyIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL2RhdGEvZXhwcmVzc2lvbi9uYW5vc3RyaW5nL3BhbmNhbmNlcl9hbm5vdGF0aW9ucy50c3YnLCAibnNjYXR0ZXJzIiA9IDYsICJzcGF0aWFsX3Jlc3VsdF9kaXJzIiA9IGxpc3QoInN0cm9tYWwiID0gJy9zaGFobGFiL2FsemhhbmcvcGlwZWxpbmVfb3V0cHV0cy9pdGhfaW1tdW5lL3NwYXRzaW0vaXRoNS9hYmMnLCAiZXBpdGhlbGlhbCIgPSAnL3NoYWhsYWIvYWx6aGFuZy9waXBlbGluZV9vdXRwdXRzL2l0aF9pbW11bmUvc3BhdHNpbS9pdGgzL2FiYycpLCAiY2xhc3NpZmllcl90eXBlIiA9ICdrbm4nLCAibWFzdGVyX3ZhcmlhbnRfZmlsZSIgPSAnL3NoYWhsYWIvYW1jcGhlcnNvbi9wcm9qZWN0cy9pdGgzL2l0aDMvbm90ZWJvb2tzL2Jlc3Bva2UvaXRoX3NudnMudHN2JywgImNsb25lX2JyYW5jaF9sZW5ndGhfZmlsZSIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL2RhdGEvaXRoL2NvbXBsZXRlL2JyYW5jaF9kYXRhLnRzdicsICJpY2djX25vcm1hbGl6ZWRfcmVhZHNfbWF0cml4IiA9ICcvc2hhaGxhYi9hbHpoYW5nL2RhdGEvSUNHQy9PVkFVX2V4cHJfbWF0cml4LnRzdicsICJpdGhfcHJvamVjdF9yZXN1bHRzIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL2l0aDMvZGF0YS9yZXN1bHRzJywgIm1tY3RtX2ZpbmFsX3BhdGllbnRfZGlyIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcmVzdWx0cy9tbWN0bV9yZXN1bHRzL2l0aF9ieS1wYXRpZW50X3dpdGgtb3YnLCAibW1jdG1fb3ZfY29tYmluZWRfc2lncGxvdCIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3Jlc3VsdHMvbW1jdG1fcmVzdWx0cy9jb21iaW5lZF9vdl9tbWN0bS9wbG90cy9vdl9zbnYtc3Zfc2lnc19tdWx0aXBhbmVsLnBkZicsICJuYW5vc3RyaW5nX2RhdGEiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9yZXN1bHRzL25hbm9zdHJpbmdfcmVzdWx0cy9pdGhfZnVsbC9xYy9saW1tYV9xdWFudGlsZS9ub3JtYWxpemVkX2V4cHJlc3Npb25fdm9hX2xhYmVsc19maWx0ZXJlZC50c3YnLCAibXVsdGl2aWV3Y2x1c3RlcmluZ19ub3RlYm9vayIgPSAnUm1kL211bHRpdmlld2NsdXN0ZXJpbmcuUm1kJywgInRjZ2FfY2xpbmljYWwiID0gJy9zaGFobGFiL2FsemhhbmcvZGF0YS9UQ0dBL3N5bmFwc2VfY2xpbkFsbF9kYXRhLnRzdicsICJtdmNsdXN0X3RpbHR5cGVzIiA9IGMoJ0VfQ0Q4X2RlbnNpdHknLCAnRV9DRDRfZGVuc2l0eScsICdFX0NEMjBfZGVuc2l0eScsICdFX1BsYXNtYV9kZW5zaXR5JywgJ1NfQ0Q4X2RlbnNpdHknLCAnU19DRDRfZGVuc2l0eScsICdTX0NEMjBfZGVuc2l0eScsICdTX1BsYXNtYV9kZW5zaXR5JyksICJpbW11bmVfdmFyaWFiaWxpdHlfbm90ZWJvb2siID0gJ1JtZC9pbW11bmVfdmFyaWFiaWxpdHkuUm1kJywgIm1vbHN1YnR5cGVfbm90ZWJvb2siID0gJ1JtZC9tb2xlY3VsYXJfc3VidHlwZXMuUm1kJywgImpfZGljdGlvbmFyeSIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3N1YnByb2plY3RzL2ltbXR5cGVyL21ldGFkYXRhL2ltZ3QvSG9tb19zYXBpZW5zX1RSQkouZmFzdGEnLCAiaWhjX3J1bjIiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9kYXRhL2loYy9jZDc5Y2QxMzhjZDY4L3ZhbGlkYXRlZF9zdGF0c193ZWlnaHRlZC5yZGF0YScsICJleGFtcGxlX2Fubm90YXRpb25zIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3BpcGVsaW5lX291dHB1dHMvaXRoX2ltbXVuZS9pZ3BhcnRpdGlvbi9ydW4xMy9maW5hbF9wYXJ0aXRpb25zL2l0aDJfMi9jbHVzdDkvYW5ub3RhdGlvbnNfZmxhZ2dlZC50c3YnLCAiYmNycGh5bG9fdGlsdHlwZXMiID0gYygnRV9DRDhfZGVuc2l0eScsICdFX0NENF9kZW5zaXR5JywgJ0VfQ0QyMF9kZW5zaXR5JywgJ0VfUGxhc21hX2RlbnNpdHknLCAnU19DRDhfZGVuc2l0eScsICdTX0NENF9kZW5zaXR5JywgJ1NfQ0QyMF9kZW5zaXR5JywgJ1NfUGxhc21hX2RlbnNpdHknLCAnVF9DRDhfZGVuc2l0eScsICdUX0NENF9kZW5zaXR5JywgJ1RfQ0QyMF9kZW5zaXR5JywgJ1RfUGxhc21hX2RlbnNpdHknKSwgImNsb25hbF9zYW1wbGVycyIgPSBjKCdITUMnLCAnTlVUUycpLCAiaWhjX3hjcl90aWx0eXBlcyIgPSBjKCdFX0NEOF9kZW5zaXR5JywgJ0VfQ0Q0X2RlbnNpdHknLCAnRV9DRDIwX2RlbnNpdHknLCAnRV9QbGFzbWFfZGVuc2l0eScsICdTX0NEOF9kZW5zaXR5JywgJ1NfQ0Q0X2RlbnNpdHknLCAnU19DRDIwX2RlbnNpdHknLCAnU19QbGFzbWFfZGVuc2l0eScsICdUX0NEOF9kZW5zaXR5JywgJ1RfQ0Q0X2RlbnNpdHknLCAnVF9DRDIwX2RlbnNpdHknLCAnVF9QbGFzbWFfZGVuc2l0eScpLCAidGNyX2RpdmVyc2l0eSIgPSAnL3NoYWhsYWIvYWx6aGFuZy9waXBlbGluZV9vdXRwdXRzL2l0aF9pbW11bmUvbWl4Y3IvbWl4Y3JfcnVucy9pdGhfMV8yXzMvbWl4Y3I1L3Bvc3Rwcm9jZXNzL1RSQi9wb3N0ZmlsdGVyX2RpdmVyc2l0eV9zdGF0cy9kaXZlcnNpdHkuc3RyaWN0LnJlc2FtcGxlZC50eHQnLCAieGNybWFwc2NhcGVfZmlsZXMiID0gYygnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3BhcGVyL3Jlc3VsdHMvaW50ZXJtZWRpYXRlcy9ydW4yL3hjcm1hcHNjYXBlLzEuc3ZnJywgJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9yZXN1bHRzL2ludGVybWVkaWF0ZXMvcnVuMi94Y3JtYXBzY2FwZS8yLnN2ZycsICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy9pbnRlcm1lZGlhdGVzL3J1bjIveGNybWFwc2NhcGUvMy5zdmcnLCAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3BhcGVyL3Jlc3VsdHMvaW50ZXJtZWRpYXRlcy9ydW4yL3hjcm1hcHNjYXBlLzQuc3ZnJywgJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9yZXN1bHRzL2ludGVybWVkaWF0ZXMvcnVuMi94Y3JtYXBzY2FwZS83LnN2ZycsICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy9pbnRlcm1lZGlhdGVzL3J1bjIveGNybWFwc2NhcGUvOS5zdmcnLCAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3BhcGVyL3Jlc3VsdHMvaW50ZXJtZWRpYXRlcy9ydW4yL3hjcm1hcHNjYXBlLzEwLnN2ZycsICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy9pbnRlcm1lZGlhdGVzL3J1bjIveGNybWFwc2NhcGUvMTEuc3ZnJywgJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9yZXN1bHRzL2ludGVybWVkaWF0ZXMvcnVuMi94Y3JtYXBzY2FwZS8xMi5zdmcnLCAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3BhcGVyL3Jlc3VsdHMvaW50ZXJtZWRpYXRlcy9ydW4yL3hjcm1hcHNjYXBlLzEzLnN2ZycsICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy9pbnRlcm1lZGlhdGVzL3J1bjIveGNybWFwc2NhcGUvMTQuc3ZnJywgJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9yZXN1bHRzL2ludGVybWVkaWF0ZXMvcnVuMi94Y3JtYXBzY2FwZS8xNS5zdmcnLCAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3BhcGVyL3Jlc3VsdHMvaW50ZXJtZWRpYXRlcy9ydW4yL3hjcm1hcHNjYXBlLzE2LnN2ZycsICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy9pbnRlcm1lZGlhdGVzL3J1bjIveGNybWFwc2NhcGUvMTcuc3ZnJyksICJ0aWxzX2Zvcl9jbHVzdGVyIiA9IGMoJ0VfQ0Q4X2RlbnNpdHknLCAnRV9DRDRfZGVuc2l0eScsICdFX0NEMjBfZGVuc2l0eScsICdFX1BsYXNtYV9kZW5zaXR5JywgJ1NfQ0Q4X2RlbnNpdHknLCAnU19DRDRfZGVuc2l0eScsICdTX0NEMjBfZGVuc2l0eScsICdTX1BsYXNtYV9kZW5zaXR5JyksICJ4Y3JfY2xvbmVzX25vdGVib29rIiA9ICdSbWQveGNyX2Nsb25lc19hbmFseXNpcy5SbWQnLCAibW9sc3VidHlwZV90aWx0eXBlcyIgPSBjKCdFX0NEOF9kZW5zaXR5JywgJ0VfQ0Q0X2RlbnNpdHknLCAnRV9DRDIwX2RlbnNpdHknLCAnRV9QbGFzbWFfZGVuc2l0eScsICdTX0NEOF9kZW5zaXR5JywgJ1NfQ0Q0X2RlbnNpdHknLCAnU19DRDIwX2RlbnNpdHknLCAnU19QbGFzbWFfZGVuc2l0eScpLCAiaWNnY19zcGVjaW1lbl9maWxlIiA9ICcvc2hhaGxhYi9hbHpoYW5nL2RhdGEvSUNHQy9zcGVjaW1lbi50c3YnLCAiaWhjX3hjcl9zdGF0c19ub3RlYm9vayIgPSAnUm1kL2loY194Y3Jfc3RhdHMuUm1kJywgIlBOR19RVUFMSVRZIiA9IDMwMCwgIm1tY3RtX3NhbXBsZV9zaWdwbG90IiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcmVzdWx0cy9tbWN0bV9yZXN1bHRzL2l0aF9ieS1zYW1wbGUvcGxvdHMvaXRoLWJ5LXNhbXBsZV9zbnYtc3Zfc2lnc19tdWx0aXBhbmVsLnBkZicsICJsb2dkaXIiID0gJy9zaGFobGFiL2FsemhhbmcvY2x1c3R0bXAvcGFwZXJhbmFseXNpczInLCAibm90ZWJvb2tfZGlyIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy93ZWInLCAiaXRoX3N0YXRfdHlwZXMiID0gYygnZW50cm9weScsICdwb3N0cHJvY2Vzc2VkX2RpdmVyZ2VuY2UnLCAnY29tYmluZWRfaXRoX25vcm1hbGl6ZWQnLCAncHJvcG9ydGlvbl9zdWJjbG9uYWwnKSwgIm1tY3RtX3BhdGllbnRfYW5jZXN0cmFsX2Rlc2NlbmRhbnRfcmVzdWx0X2RpciIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3Jlc3VsdHMvbW1jdG1fcmVzdWx0cy9pdGhfYnktcGF0aWVudC1hbmNlc3RyeS9vdXRwdXQnLCAibmNsdXN0cyIgPSAzLCAibmVvYW50aWdlbl9lZGl0aW5nX25vdGVib29rIiA9ICdSbWQvaW1tdW5vZWRpdGluZy5SbWQnLCAiZGVmYXVsdF9zYW1wbGVyIiA9ICdITUMnLCAiY2xvbmFsX2ZpZ3VyZV90ZW1wbGF0ZSIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3BhcGVyL2FuYWx5c2lzL3RlbXBsYXRlcy9jbG9uYWxfZmlndXJlLnN2ZycsICJwcmV2YWxlbmNlX3RocmVzaG9sZCIgPSAwLjAxLCAibWFwc2NhcGVfbm90ZWJvb2siID0gJ1JtZC9tYXBzY2FwZS5SbWQnLCAibWFzdGVyX2JyZWFrcG9pbnRfZmlsZSIgPSAnL3NoYWhsYWIvYW1jcGhlcnNvbi9wcm9qZWN0cy9pdGgzL2l0aDMvbm90ZWJvb2tzL2Jlc3Bva2UvaXRoX2JyZWFrcG9pbnRzLnRzdicsICJpY2djX2NsaW5pY2FsIiA9ICcvc2hhaGxhYi9hbHpoYW5nL2RhdGEvSUNHQy9kb25vci5PVi1BVS50c3YnLCAiYmNyX2Nsb25vdHlwZXMiID0gJy9zaGFobGFiL2FsemhhbmcvcGlwZWxpbmVfb3V0cHV0cy9pdGhfaW1tdW5lL21peGNyL21peGNyX3J1bnMvaXRoXzFfMl8zL21peGNyNS9jbG9ub3R5cGVzL0lHSF9jbG9ub3R5cGVzX2ZpbHRlcmVkLnR4dCcsICJzcGF0aWFsX25vdGVib29rIiA9ICdSbWQvc3BhdGlhbF9hbmFseXNpcy5SbWQnLCAiZGIiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9tZXRhZGF0YS9kYi9pbW11bmVfcHJvamVjdC5zcWxpdGUzJywgImRyaXZlcl9hbmFseXNpc19ub3RlYm9vayIgPSAnUm1kL2RyaXZlcl9hbmFseXNpcy5SbWQnLCAiZHJpdmVyX21hcCIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3N1YnByb2plY3RzL2RyaXZlcnMvZGF0YS9nZW5lX2xpc3RfbWFwcGVkLmJlZCcsICJ0aWxfY2xhc3NpZmllcl9ub3RlYm9vayIgPSAnUm1kL3RpbF9jbGFzc2lmaWVyLlJtZCcsICJtdXRhdGlvbl9zaWduYXR1cmVfbm90ZWJvb2siID0gJ1JtZC9tdXRhdGlvbl9zaWduYXR1cmVzLlJtZCcsICJ3YW5nX2ZiaV9zdGF0dXMiID0gJy9zaGFobGFiL2FsemhhbmcvZGF0YS9JQ0dDL25nLjM4NDktUzEyLnR4dCcsICJ2X2RpY3Rpb25hcnkiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9zdWJwcm9qZWN0cy9pbW10eXBlci9tZXRhZGF0YS9pbWd0L0hvbW9fc2FwaWVuc19UUkJWLmZhc3RhJywgIml0aF90aWxfbm90ZWJvb2siID0gJ1JtZC9pdGhfdGlsX2RlbnNpdGllcy5SbWQnLCAibW1jdG1fcGF0aWVudF9hZF9zaWdwbG90IiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcmVzdWx0cy9tbWN0bV9yZXN1bHRzL2l0aF9ieS1wYXRpZW50LWFuY2VzdHJ5L3Bsb3RzL2l0aC1ieS1wYXRpZW50LWFuY2VzdHJ5X3Nudi1zdl9zaWdzX211bHRpcGFuZWwucGRmJywgImljZ2NfbW9sZWN1bGFyX3N1YnR5cGVzIiA9ICcvc2hhaGxhYi9hbHpoYW5nL2RhdGEvSUNHQy9pY2djX3ByaW1hcnlfdHVtb3VyX3N1YnR5cGVzLnRzdicsICJzaXRlX2ZpbGUiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9hbmFseXNpcy9SbWQvX3NpdGUueW1sJywgInByb3BvcnRpb25fc3ViY2xvbmFsX2ZpbGUiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9kYXRhL2l0aC9jb21wbGV0ZS9vbGRfcHJvcG9ydGlvbl9zdWJjbG9uYWwudHN2JywgInRhYmxlX2RpciIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3BhcGVyL3Jlc3VsdHMvdGFibGVzL3J1bjInLCAiaWNnY19leHByX21lbHRlZCIgPSAnL3NoYWhsYWIvYWx6aGFuZy9kYXRhL0lDR0MvT1ZBVV9leHByX21lbHRlZC50c3YnLCAiUE5HX0RFTlNJVFkiID0gMzAwLCAibGlicmFyeV9zaXplcyIgPSAnL3NoYWhsYWIvYWx6aGFuZy9waXBlbGluZV9vdXRwdXRzL2l0aF9pbW11bmUvbWl4Y3IvbWl4Y3JfcnVucy9pdGhfMV8yXzMvbWl4Y3I1L2xpYnJhcnlfc2l6ZXMudHN2JywgIm12Y2x1c3RfbmNsdXN0IiA9IDMsICJ4Y3JfcWNfbm90ZWJvb2siID0gJ1JtZC9yZXBsaWNhdGVzLlJtZCcsICJiZW5jaG1hcmtkaXIiID0gJy9zaGFobGFiL2FsemhhbmcvYmVuY2htYXJrcy9wYXBlcmFuYWx5c2lzMicsICJrbm93bl9zdWJ0eXBlX2ZpbGUiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9kYXRhL2V4cHJlc3Npb24vYXJyYXkvc3VidHlwZXMva25vd25fc3VidHlwZXMudHN2JywgInNhZF9ub3RlYm9vayIgPSAnUm1kL3NwZWNpZXNfYWJ1bmRhbmNlX2Rpc3RyaWJ1dGlvbnMuUm1kJywgIm1tY3RtX2FuY2VzdHJhbF9kZXNjZW5kYW50X3Jlc3VsdF9kaXIiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9yZXN1bHRzL21tY3RtX3Jlc3VsdHMvaXRoX2J5LWFuY2VzdHJ5LXNhbXBsZS9vdXRwdXQnLCAibW1jdG1fc2FtcGxlX2FkX3NpZ3Bsb3QiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9yZXN1bHRzL21tY3RtX3Jlc3VsdHMvaXRoX2J5LWFuY2VzdHJ5LXNhbXBsZS9wbG90cy9pdGgtYnktYW5jZXN0cmFsLXNhbXBsZV9zbnYtc3Zfc2lnc19tdWx0aXBhbmVsLnBkZicsICJiY3JwaHlsb19jb3JyZWxhdGlvbnNfbm90ZWJvb2siID0gJ1JtZC9iY3JfcGh5bG9fY29ycmVsYXRpb25zLlJtZCcsICJiY3JwaHlsb19leGFtcGxlc19ub3RlYm9vayIgPSAnUm1kL2Jjcl9waHlsb19leGFtcGxlcy5SbWQnLCAiY2xvbmVfdHJlZV9maWxlIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvZGF0YS9pdGgvY29tcGxldGUvdHJlZV9kYXRhLnRzdicsICJpbmRleF9ub3RlYm9vayIgPSAnUm1kL2luZGV4LlJtZCcsICJpdGhfc3RhdHNfZmlsZSIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL2RhdGEvaXRoL2NvbXBsZXRlL2Nsb25hbF9tZWFzdXJlcy50c3YnLCAiaW1tdHlwZXJfbW9kZWxzIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcmVzdWx0cy9pbW10eXBlcl9yZXN1bHRzL2tsYXJlbmJlZWsvYWFfdmovZ3JhZGJvb3N0JywgIm1tY3RtX2ZpbmFsX3BhdGllbnRfc2lncGxvdCIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3Jlc3VsdHMvbW1jdG1fcmVzdWx0cy9pdGhfYnktcGF0aWVudF93aXRoLW92L3Bsb3RzL2l0aC1ieS1wYXRpZW50X3Nudi1zdl9zaWdzX211bHRpcGFuZWwucGRmJywgIm1tY3RtX292X2NvbWJpbmVkX3Jlc3VsdF9kaXIiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9yZXN1bHRzL21tY3RtX3Jlc3VsdHMvY29tYmluZWRfb3ZfbW1jdG0vb3V0cHV0JywgInNtb290aF90eXBlIiA9ICdsb2VzcycsICJpbW10eXBlcl9sZW5ndGhzIiA9ICcxMSAxMiAxMyAxNCAxNSAxNiAxNyAxOCcsICJpdGhfc3RhdGlzdGljc19ub3RlYm9vayIgPSAnUm1kL2l0aF9zdGF0aXN0aWNzLlJtZCcsICJpbnRlcm1lZGlhdGVfZGlyIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy9pbnRlcm1lZGlhdGVzL3J1bjInLCAieGNyX21hcHBpbmdfbm90ZWJvb2siID0gJ1JtZC94Y3JfbWFwcGluZy5SbWQnLCAidGNnYV9vdl9hbm5vdGF0aW9ucyIgPSAnL3NoYWhsYWIvYWx6aGFuZy9kYXRhL1RDR0EvdGNnYV9vdl9hbm5vdGF0aW9uX3N1cDEzLnR4dCcsICJ0Y2dhX2V4cHJfbWF0cml4IiA9ICcvc2hhaGxhYi9hbHpoYW5nL2RhdGEvVENHQS9leHByX21hdHJpeF9ub3JtYWxpemVfc3RhbmRhcmRpemVfbm9kdXBsaWNhdGVzLnRzdicsICJ4Y3JtYXBzY2FwZV9ub3RlYm9vayIgPSAnUm1kL3hjcm1hcHNjYXBlLlJtZCcsICJwaGVub3R5cGVfdGhyZXNob2xkIiA9IDAuODUpLAogICAgcnVsZSA9ICdtb2xlY3VsYXJfc3VidHlwZV9hbmFseXNpcycKKQojIyMjIyMjIyBPcmlnaW5hbCBzY3JpcHQgIyMjIyMjIyMjCgogICAgICAgICAgICAgICAgICAgICAgICBgYGAKCgpgYGB7ciBnbG9iYWxfY2h1bmtfb3B0aW9ucywgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCB0aWR5PVRSVUUsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UpCmBgYAoKCmBgYHtyfQpsaWJyYXJ5KGl0aGkudXRpbHMpCmxvYWRfYmFzZV9saWJzKCkKbGlicmFyeShuZXN0ZWRSYW5rc1Rlc3QpCgpsaWJyYXJ5KGl0aGkubWV0YSkKbGlicmFyeShpdGhpLnhjcikKbGlicmFyeShpdGhpLmloYykKbGlicmFyeShpdGhpLmV4cHIpCmxpYnJhcnkoaXRoaS5jbG9uZXMpCmxpYnJhcnkobmFub3Rvb2xzKQpgYGAKCiMjIENvbG91ciBwYWxldHRlcwoKYGBge3J9CnBhbF9wYXRpZW50IDwtIHNlbGVjdF9wYWxldHRlKCJwYXRpZW50IikKYGBgCgojIyBQYXJhbWV0ZXJzCgpgYGB7cn0KZGJfcGF0aCA8LSBzbmFrZW1ha2VAcGFyYW1zJGRiCgptb2xlY3VsYXJfc3VidHlwZV9maWxlIDwtIHNuYWtlbWFrZUBpbnB1dCRtb2xlY3VsYXJfc3VidHlwZXMKCmloY190YWJsZV9wYXRoIDwtIHNuYWtlbWFrZUBpbnB1dCRpaGNfdGFibGUKdGlsdHlwZXMgPC0gdW5saXN0KHNuYWtlbWFrZUBwYXJhbXMkdGlsdHlwZXMpCgp0Y3JfZGl2ZXJzaXR5X2ZpbGUgPC0gc25ha2VtYWtlQGlucHV0JHRjcl9kaXZlcnNpdHkKYmNyX2RpdmVyc2l0eV9maWxlIDwtIHNuYWtlbWFrZUBpbnB1dCRiY3JfZGl2ZXJzaXR5Cgp4Y3JfdGFibGVfcGF0aCA8LSBzbmFrZW1ha2VAaW5wdXQkeGNyX3RhYmxlCgpuYW5vc3RyaW5nX2RhdGFfcGF0aCA8LSBzbmFrZW1ha2VAaW5wdXQkbmFub3N0cmluZ19kYXRhCm5hbm9zdHJpbmdfYW5ub3RhdGlvbnNfcGF0aCA8LSBzbmFrZW1ha2VAaW5wdXQkbmFub3N0cmluZ19hbm5vdGF0aW9ucwoKY2xvbmVfdHJlZV9maWxlIDwtIHNuYWtlbWFrZUBpbnB1dCRjbG9uZV90cmVlX2ZpbGUKY2xvbmVfcHJldmFsZW5jZV9maWxlIDwtIHNuYWtlbWFrZUBpbnB1dCRjbG9uZV9wcmV2YWxlbmNlX2ZpbGUKY2xvbmVfYnJhbmNoX2xlbmd0aF9maWxlIDwtIHNuYWtlbWFrZUBpbnB1dCRjbG9uZV9icmFuY2hfbGVuZ3RoX2ZpbGUKYGBgCgojIyBNZXRhZGF0YQoKYGBge3J9CmRiIDwtIHNyY19zcWxpdGUoZGJfcGF0aCwgY3JlYXRlPUZBTFNFKQpzYW1wbGVzIDwtIGNvbGxlY3QodGJsKGRiLCAic2FtcGxlcyIpKQpgYGAKCgojIyBNb2xlY3VsYXIgc3VidHlwZSBjbGFzc2lmaWNhdGlvbgoKYGBge3J9Cm1vbHN1YnR5cGVzIDwtIGZyZWFkKG1vbGVjdWxhcl9zdWJ0eXBlX2ZpbGUpCm1vbHN1YnR5cGVzIDwtIHNldE5hbWVzKG1vbHN1YnR5cGVzLCBjKCJjb25kZW5zZWRfaWQiLCAic3VidHlwZSIpKQpgYGAKCiMjIyBDb3VudHMKCmBgYHtyfQp0YWJsZShtb2xzdWJ0eXBlcyRzdWJ0eXBlKQpgYGAKCiMjIElIQwoKYGBge3J9CmloY190YWJsZSA8LSBmcmVhZChpaGNfdGFibGVfcGF0aCkKaWhjX3RhYmxlJHBhdGllbnRfaWQgPC0gZmFjdG9yKGloY190YWJsZSRwYXRpZW50X2lkKQoKaWhjbSA8LSBtZWx0KGloY190YWJsZSwgaWQudmFycyA9IGMoImNvbmRlbnNlZF9pZCIsICJwYXRpZW50X2lkIiksIG1lYXN1cmUudmFycyA9IHRpbHR5cGVzLAogICAgICAgICAgICAgdmFyaWFibGUubmFtZSA9ICJ0aWx0eXBlIiwgdmFsdWUubmFtZSA9ICJkZW5zaXR5IikKCmloY21fc3VidHlwZSA8LSBtZXJnZShtb2xzdWJ0eXBlcywgaWhjbSwgYnk9YygiY29uZGVuc2VkX2lkIikpCmBgYAoKYGBge3IsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTR9CnN0YXRzIDwtIHNldE5hbWVzKGRkcGx5KGloY21fc3VidHlwZSwgLih0aWx0eXBlKSwgZnVuY3Rpb24oeCkgewogIHJlcyA8LSBrcnVza2FsLnRlc3QoZGVuc2l0eSB+IGZhY3RvcihzdWJ0eXBlKSwgZGF0YT14KQogIHB2YWx1ZSA8LSByZXMkcC52YWx1ZQogIGVxIDwtIHN1YnN0aXR1dGUoaXRhbGljKFApPT1wLCBsaXN0KHA9Zm9ybWF0KHB2YWx1ZSwgZGlnaXRzPTMpKSkKICByZXR1cm4oYXMuY2hhcmFjdGVyKGFzLmV4cHJlc3Npb24oZXEpKSkKfSksIGMoInRpbHR5cGUiLCAicC52YWx1ZSIpKQoKZ2dwbG90KGloY21fc3VidHlwZSwgYWVzKHg9c3VidHlwZSwgeT1kZW5zaXR5KSkgKyBnZW9tX2JveHBsb3QoKSArIGdlb21faml0dGVyKHBvc2l0aW9uID0gcG9zaXRpb25faml0dGVyKHdpZHRoPTAuMiwgaGVpZ2h0PTApLCBhZXMoY29sb3VyPXBhdGllbnRfaWQpKSArIHRoZW1lX2J3KCkgKyB0aGVtZV9QdWJsaWNhdGlvbigpICsgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IHBhbF9wYXRpZW50KSArIGZhY2V0X3dyYXAofiB0aWx0eXBlLCBzY2FsZXM9ImZyZWUiLCBucm93PTIpICsgZ2VvbV90ZXh0KGRhdGE9c3RhdHMsIGFlcyh4PUluZiwgeT1JbmYsIGxhYmVsPXAudmFsdWUpLCBoanVzdD0xLjEsIHZqdXN0PTEuNSwgc2l6ZT0zLCBwYXJzZT1UUlVFKQpgYGAKCgojIyBUQ1IvQkNSIGRpdmVyc2l0eQoKYGBge3J9CmRpdmVyc2l0eV9maWxlcyA8LSBsaXN0KHRjcj10Y3JfZGl2ZXJzaXR5X2ZpbGUsCiAgICAgICAgICAgICAgICAgICAgICAgIGJjcj1iY3JfZGl2ZXJzaXR5X2ZpbGUpCgpkaXZlcnNpdHkgPC0gbGFwcGx5KGRpdmVyc2l0eV9maWxlcywgZnVuY3Rpb24oZikgewogIGRhdCA8LSByZWFkX3hjcl9kaXZlcnNpdHlfZmlsZShmLCBkYl9wYXRoKQogIG1lcmdlKGRhdCwgbW9sc3VidHlwZXMsIGJ5PWMoImNvbmRlbnNlZF9pZCIpKQp9KQoKeGNyX3RhYmxlIDwtIHJlYWRfY2xvbm90eXBlcyh4Y3JfdGFibGVfcGF0aCwgZHVwbGljYXRlcyA9IEZBTFNFLCBkYl9wYXRoKQp4Y3JfdGFibGUkY29uZGVuc2VkX2lkIDwtIGFzLmNoYXJhY3Rlcih4Y3JfdGFibGUkY29uZGVuc2VkX2lkKQpwYXRpZW50cyA8LSB1bmlxdWUoeGNyX3RhYmxlJHBhdGllbnRfaWQpCgp0Y3Jfc2VnbWVudF90eXBlIDwtICJUUkIiCmJjcl9zZWdtZW50X3R5cGUgPC0gIklHSCIKaWRfdHlwZSA8LSAiY29uZGVuc2VkX2lkIgoKIyBub3QgYWN0dWFsbHkgYmV0YSBkaXZlcnNpdHkgYnV0IHN0aWxsIGZ1bgpiZXRhcyA8LSByYmluZC5maWxsKGxhcHBseShwYXRpZW50cywgZnVuY3Rpb24ocGF0aWVudCkgewogIHRjcl9jbG9ub3R5cGVzIDwtIHN1YnNldCh4Y3JfdGFibGUsIHR5cGUgPT0gdGNyX3NlZ21lbnRfdHlwZSAmIHBhdGllbnRfaWQgPT0gcGF0aWVudCkKICBiY3JfY2xvbm90eXBlcyA8LSBzdWJzZXQoeGNyX3RhYmxlLCB0eXBlID09IGJjcl9zZWdtZW50X3R5cGUgJiBwYXRpZW50X2lkID09IHBhdGllbnQpCiAgdGNyX2Nyb3NzX3RhYmxlIDwtIGNyb3NzX3RhYnVsYXRlKHRjcl9jbG9ub3R5cGVzLCBpZF90eXBlID0gaWRfdHlwZSkKICBiY3JfY3Jvc3NfdGFibGUgPC0gY3Jvc3NfdGFidWxhdGUoYmNyX2Nsb25vdHlwZXMsIGlkX3R5cGUgPSBpZF90eXBlKQogIAogIGNyb3NzX3RhYmxlcyA8LSBsaXN0KHRjcj10Y3JfY3Jvc3NfdGFibGUsIGJjcj1iY3JfY3Jvc3NfdGFibGUpCiAgCiAgYmV0YXZhbHMgPC0gcmJpbmQuZmlsbChsYXBwbHkoc2VxX2Fsb25nKGNyb3NzX3RhYmxlcyksIGZ1bmN0aW9uKGkpIHsKICAgIGNyb3NzX3RhYmxlIDwtIGNyb3NzX3RhYmxlc1tbaV1dCiAgICBkZiA8LSBjYmluZCh0eXBlPW5hbWVzKGNyb3NzX3RhYmxlcylbaV0sIGNvbXB1dGVfYmV0YV9kaXZlcnNpdHkoY3Jvc3NfdGFibGUsIG1vZGUgPSAidW5pb24iLCBtZWFzdXJlID0gImNvdW50IikpCiAgICByZXR1cm4oZGYpCiAgfSkpCiAgcmV0dXJuKGNiaW5kKHBhdGllbnRfaWQ9cGF0aWVudCwgYmV0YXZhbHMpKQp9KSkKYmV0YXNfbGlzdCA8LSBzcGxpdChiZXRhcywgZiA9IGJldGFzJHR5cGUpCgpjb21wdXRlX2RpdmVyc2l0eV9pbmRleCA8LSBmdW5jdGlvbih4Y3JfdGFibGUsIHR5cGUgPSAicmFyZW5lc3MiLCByYXJlX3RocmVzaG9sZCA9IDFlLTQpIHsKICBpZiAodHlwZSA9PSAicmFyZW5lc3MiKSB7CiAgICBQUkVWQUxFTkNFX1RZUEUgPC0gImZyZXEiCiAgICAKICAgIHJlcyA8LSB4Y3JfdGFibGUgJT4lIGdyb3VwX2J5XyguZG90cz1jKCJjb25kZW5zZWRfaWQiLCAicGF0aWVudF9pZCIsICJ0eXBlIikpICU+JQogICAgICBzdW1tYXJpc2VfKHJhcmVuZXNzID0gbGF6eWV2YWw6OmludGVycCh+c3VtKHZhclt2YXIgPCByYXJlX3RocmVzaG9sZF0pLCB2YXIgPSBhcy5uYW1lKFBSRVZBTEVOQ0VfVFlQRSkpKQogICAgcmVzIDwtIHNwbGl0KHJlcywgZj1yZXMkdHlwZSkKICB9IGVsc2UgaWYgKHR5cGUgPT0gImJlcmdlcl9wYXJrZXIiKSB7CiAgICByZXMgPC0geGNyX3RhYmxlICU+JSBncm91cF9ieV8oLmRvdHM9YygiY29uZGVuc2VkX2lkIiwgInBhdGllbnRfaWQiLCAidHlwZSIpKSAlPiUKICAgICAgc3VtbWFyaXNlKGJlcmdlcl9wYXJrZXIgPSAxL21heChmcmVxKSkKICAgIHJlcyA8LSBzcGxpdChyZXMsIGY9cmVzJHR5cGUpCiAgfQogIHJldHVybihyZXMpCn0KCmNkZl9jbG9ub3R5cGVfZnJlcXMgPC0gZnVuY3Rpb24oeGNyX3RhYmxlLCBzZWdtZW50ID0gIlRSQiIpIHsKICBnZ3Bsb3QoeGNyX3RhYmxlLCBhZXMoeD1mcmVxKSkgKyBzdGF0X2VjZGYoZ2VvbT0ic3RlcCIpICsgc2NhbGVfeF9sb2cxMCgpICsgZmFjZXRfd3JhcCh+IGNvbmRlbnNlZF9pZCwgbmNvbCA9IDUpCn0KCnBsb3RfZGl2ZXJzaXR5X3N1YnR5cGUgPC0gZnVuY3Rpb24oZGl2ZXJzaXR5LCBjb2wpIHsKICBpZ25vcmUgPC0gbGFwcGx5KGRpdmVyc2l0eSwgZnVuY3Rpb24oZGYpIHsKICAgIHAgPC0gZ2dwbG90KGRmLCBhZXNfc3RyaW5nKHg9InN1YnR5cGUiLCB5PWNvbCkpICsgZ2VvbV9ib3hwbG90KCkgKyBnZW9tX2ppdHRlcihwb3NpdGlvbj1wb3NpdGlvbl9qaXR0ZXIod2lkdGg9MC4yLCBoZWlnaHQ9MCksYWVzKGNvbG91cj1wYXRpZW50X2lkKSkgKyB0aGVtZV9idygpICsgdGhlbWVfUHVibGljYXRpb24oKSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBwYWxfcGF0aWVudCkgKyB4bGFiKCJTdWJ0eXBlIikgKyB5bGFiKCJEaXZlcnNpdHkiKQogICAgcHJpbnQocCkKICB9KQp9CmBgYAoKIyMjIFJpY2huZXNzCgpgYGB7cn0KcGxvdF9kaXZlcnNpdHlfc3VidHlwZShkaXZlcnNpdHksICJvYnNlcnZlZERpdmVyc2l0eV9tZWFuIikKYGBgCgojIyMgU2hhbm5vbiBlbnRyb3B5CgpgYGB7cn0KcGxvdF9kaXZlcnNpdHlfc3VidHlwZShkaXZlcnNpdHksICJzaGFubm9uV2llbmVySW5kZXhfbWVhbiIpCmBgYAoKIyMjIERpdmVyZ2VuY2UKCmBgYHtyfQpwbG90X2RpdmVyc2l0eV9zdWJ0eXBlKGRpdmVyc2l0eSwgImludmVyc2VTaW1wc29uSW5kZXhfbWVhbiIpCmBgYAoKIyMjIEQ1MCBpbmRleAoKYGBge3J9CnBsb3RfZGl2ZXJzaXR5X3N1YnR5cGUoZGl2ZXJzaXR5LCAiZDUwSW5kZXhfbWVhbiIpCmBgYAoKIyMjIFJhcmVuZXNzCgpgYGB7cn0KcmFyZW5lc3MgPC0gY29tcHV0ZV9kaXZlcnNpdHlfaW5kZXgoeGNyX3RhYmxlLCB0eXBlID0gInJhcmVuZXNzIiwgcmFyZV90aHJlc2hvbGQgPSAxZS00KQpyYXJlbmVzcyA8LSBsYXBwbHkocmFyZW5lc3MsIGZ1bmN0aW9uKHgpIHsKICB4JHBhdGllbnRfaWQgPC0gYXMuY2hhcmFjdGVyKHgkcGF0aWVudF9pZCkKICBtZXJnZSh4LCBtb2xzdWJ0eXBlcywgYnk9YygiY29uZGVuc2VkX2lkIikpCn0pCnBsb3RfZGl2ZXJzaXR5X3N1YnR5cGUocmFyZW5lc3MsICJyYXJlbmVzcyIpCmBgYAoKIyMjIEJlcmdlci1QYXJrZXIKCmBgYHtyfQpkb21pbmFuY2UgPC0gY29tcHV0ZV9kaXZlcnNpdHlfaW5kZXgoeGNyX3RhYmxlLCB0eXBlID0gImJlcmdlcl9wYXJrZXIiKQpkb21pbmFuY2UgPC0gbGFwcGx5KGRvbWluYW5jZSwgZnVuY3Rpb24oeCkgewogIHgkcGF0aWVudF9pZCA8LSBhcy5jaGFyYWN0ZXIoeCRwYXRpZW50X2lkKQogIG1lcmdlKHgsIG1vbHN1YnR5cGVzLCBieT1jKCJjb25kZW5zZWRfaWQiKSkKfSkKcGxvdF9kaXZlcnNpdHlfc3VidHlwZShkb21pbmFuY2UsICJiZXJnZXJfcGFya2VyIikKYGBgCgojIyMgU2FtcGxlLXNwZWNpZmljIGNsb25vdHlwZSBwcm9wb3J0aW9uCgpgYGB7cn0KYmV0YXNfbGlzdCA8LSBsYXBwbHkoYmV0YXNfbGlzdCwgZnVuY3Rpb24oeCkgewogIHgkcGF0aWVudF9pZCA8LSBhcy5jaGFyYWN0ZXIoeCRwYXRpZW50X2lkKQogIG1lcmdlKHgsIG1vbHN1YnR5cGVzLCBieT1jKCJjb25kZW5zZWRfaWQiKSkKfSkKcGxvdF9kaXZlcnNpdHlfc3VidHlwZShiZXRhc19saXN0LCAicHJvcF91bmlxdWUiKQpgYGAKCiMjIyBDREZzCgpgYGB7ciwgZmlnLndpZHRoPTIwLCBmaWcuaGVpZ2h0PTEwMH0KY2RmX2Nsb25vdHlwZV9mcmVxcyh4Y3JfdGFibGUsICJUUkIiKQpgYGAKCmBgYHtyLCBmaWcud2lkdGg9MjAsIGZpZy5oZWlnaHQ9MTAwfQpjZGZfY2xvbm90eXBlX2ZyZXFzKHhjcl90YWJsZSwgIklHSCIpCmBgYAoKIyMgTmFub3N0cmluZwoKYGBge3J9CmV4cHJzIDwtIGZyZWFkKG5hbm9zdHJpbmdfZGF0YV9wYXRoKQpsYWJlbHMgPC0gZnJlYWQobmFub3N0cmluZ19hbm5vdGF0aW9uc19wYXRoKQoKY2VsbHR5cGVfbWF0cml4IDwtIHJvd25hbWVzX3RvX2NvbHVtbihhcy5kYXRhLmZyYW1lKHQoY3JlYXRlX2NlbGx0eXBlX21hdHJpeChleHBycywgbGFiZWxzLCBkYl9wYXRoKSkpLCB2YXIgPSAiY29uZGVuc2VkX2lkIikKY2VsbHR5cGVfdmFycyA8LSBjb2xuYW1lcyhjZWxsdHlwZV9tYXRyaXgpWyFjb2xuYW1lcyhjZWxsdHlwZV9tYXRyaXgpID09ICJjb25kZW5zZWRfaWQiXQoKY2VsbHR5cGVfbWF0cml4X21lbHRlZCA8LSBtZWx0KGNlbGx0eXBlX21hdHJpeCwgaWQudmFycyA9IGMoImNvbmRlbnNlZF9pZCIpLCBtZWFzdXJlLnZhcnMgPSBjZWxsdHlwZV92YXJzLAogICAgdmFyaWFibGUubmFtZSA9ICJ0eXBlIiwgdmFsdWUubmFtZSA9ICJleHByIikKCmNlbGx0eXBlX3N1YnR5cGUgPC0gbWVyZ2UoY2VsbHR5cGVfbWF0cml4X21lbHRlZCwgbW9sc3VidHlwZXMsIGJ5PWMoImNvbmRlbnNlZF9pZCIpKQpjZWxsdHlwZV9zdWJ0eXBlJHBhdGllbnRfaWQgPC0gZmFjdG9yKG1hcF9pZChjZWxsdHlwZV9zdWJ0eXBlJGNvbmRlbnNlZF9pZCwgZnJvbT0iY29uZGVuc2VkX2lkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0bz0icGF0aWVudF9pZCIsIGRiX3BhdGgpKQpgYGAKCmBgYHtyLCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD04fQpzdGF0cyA8LSBzZXROYW1lcyhkZHBseShjZWxsdHlwZV9zdWJ0eXBlLCAuKHR5cGUpLCBmdW5jdGlvbih4KSB7CiAgcmVzIDwtIGtydXNrYWwudGVzdChleHByIH4gZmFjdG9yKHN1YnR5cGUpLCBkYXRhPXgpCiAgcHZhbHVlIDwtIHJlcyRwLnZhbHVlCiAgZXEgPC0gc3Vic3RpdHV0ZShpdGFsaWMoUCk9PXAsIGxpc3QocD1mb3JtYXQocHZhbHVlLCBkaWdpdHM9MykpKQogIHJldHVybihhcy5jaGFyYWN0ZXIoYXMuZXhwcmVzc2lvbihlcSkpKQp9KSwgYygidHlwZSIsICJwLnZhbHVlIikpCgpnZ3Bsb3QoY2VsbHR5cGVfc3VidHlwZSwgYWVzKHg9c3VidHlwZSwgeT1leHByKSkgKyBnZW9tX2JveHBsb3QoKSArIGdlb21faml0dGVyKHBvc2l0aW9uID0gcG9zaXRpb25faml0dGVyKHdpZHRoPTAuMiwgaGVpZ2h0PTApLCBhZXMoY29sb3VyPXBhdGllbnRfaWQpKSArIHRoZW1lX2J3KCkgKyB0aGVtZV9QdWJsaWNhdGlvbigpICsgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IHBhbF9wYXRpZW50KSArIGZhY2V0X3dyYXAofiB0eXBlLCBzY2FsZXM9ImZyZWUiLCBucm93PTQpICsgZ2VvbV90ZXh0KGRhdGE9c3RhdHMsIGFlcyh4PUluZiwgeT1JbmYsIGxhYmVsPXAudmFsdWUpLCBoanVzdD0xLjEsIHZqdXN0PTEuNSwgc2l6ZT0zLCBwYXJzZT1UUlVFKQpgYGAKCgpgYGB7cn0KcGF0aHdheV9tYXRyaXggPC0gcm93bmFtZXNfdG9fY29sdW1uKGFzLmRhdGEuZnJhbWUodChjcmVhdGVfcGF0aHdheV9tYXRyaXgoZXhwcnMsIGxhYmVscywgZGJfcGF0aCkpKSwgdmFyID0gImNvbmRlbnNlZF9pZCIpCnBhdGh3YXlfdmFycyA8LSBjb2xuYW1lcyhwYXRod2F5X21hdHJpeClbIWNvbG5hbWVzKHBhdGh3YXlfbWF0cml4KSA9PSAiY29uZGVuc2VkX2lkIl0KCnBhdGh3YXlfbWF0cml4X21lbHRlZCA8LSBtZWx0KHBhdGh3YXlfbWF0cml4LCBpZC52YXJzID0gYygiY29uZGVuc2VkX2lkIiksIG1lYXN1cmUudmFycyA9IHBhdGh3YXlfdmFycywgdmFyaWFibGUubmFtZSA9ICJ0eXBlIiwgdmFsdWUubmFtZSA9ICJleHByIikKCnBhdGh3YXlfc3VidHlwZSA8LSBtZXJnZShwYXRod2F5X21hdHJpeF9tZWx0ZWQsIG1vbHN1YnR5cGVzLCBieT1jKCJjb25kZW5zZWRfaWQiKSkKcGF0aHdheV9zdWJ0eXBlJHBhdGllbnRfaWQgPC0gZmFjdG9yKG1hcF9pZChwYXRod2F5X3N1YnR5cGUkY29uZGVuc2VkX2lkLCBmcm9tPSJjb25kZW5zZWRfaWQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvPSJwYXRpZW50X2lkIiwgZGJfcGF0aCkpCmBgYAoKYGBge3IsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTh9CnN0YXRzIDwtIHNldE5hbWVzKGRkcGx5KHBhdGh3YXlfc3VidHlwZSwgLih0eXBlKSwgZnVuY3Rpb24oeCkgewogIHJlcyA8LSBrcnVza2FsLnRlc3QoZXhwciB+IGZhY3RvcihzdWJ0eXBlKSwgZGF0YT14KQogIHB2YWx1ZSA8LSByZXMkcC52YWx1ZQogIGVxIDwtIHN1YnN0aXR1dGUoaXRhbGljKFApPT1wLCBsaXN0KHA9Zm9ybWF0KHB2YWx1ZSwgZGlnaXRzPTMpKSkKICByZXR1cm4oYXMuY2hhcmFjdGVyKGFzLmV4cHJlc3Npb24oZXEpKSkKfSksIGMoInR5cGUiLCAicC52YWx1ZSIpKQoKZ2dwbG90KHBhdGh3YXlfc3VidHlwZSwgYWVzKHg9c3VidHlwZSwgeT1leHByKSkgKyBnZW9tX2JveHBsb3QoKSArIGdlb21faml0dGVyKHBvc2l0aW9uID0gcG9zaXRpb25faml0dGVyKHdpZHRoPTAuMiwgaGVpZ2h0PTApLCBhZXMoY29sb3VyPXBhdGllbnRfaWQpKSArIHRoZW1lX2J3KCkgKyB0aGVtZV9QdWJsaWNhdGlvbigpICsgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IHBhbF9wYXRpZW50KSArIGZhY2V0X3dyYXAofiB0eXBlLCBzY2FsZXM9ImZyZWUiLCBucm93PTQpICsgZ2VvbV90ZXh0KGRhdGE9c3RhdHMsIGFlcyh4PUluZiwgeT1JbmYsIGxhYmVsPXAudmFsdWUpLCBoanVzdD0xLjEsIHZqdXN0PTEuNSwgc2l6ZT0zLCBwYXJzZT1UUlVFKQpgYGAKCiMjIFJlbGF0aW9uIHRvIGNsb25hbCBjb21wb3NpdGlvbgoKSGVyZSB3ZSdsbCByZWxhdGUgaW50cmFwYXRpZW50IGRpZmZlcmVuY2VzIGluIG1vbGVjdWxhciBzdWJ0eXBlIHRvIGRpZmZlcmVuY2VzIGluIGNsb25hbCBjb21wb3NpdGlvbi4gClF1ZXN0aW9uczoKKiBBcmUgdHVtb3VycyB3aXRoIGRpZmZlcmVudCBtb2xlY3VsYXIgc3VidHlwZXMgbW9yZSBjbG9uYWxseSBkaWZmZXJlbnQ/CgpgYGB7cn0KdHJlZV9icmFuY2hfZGF0YSA8LSByZWFkX2Nsb25lX3RyZWVfZGF0YShjbG9uZV90cmVlX2ZpbGUsIGNsb25lX2JyYW5jaF9sZW5ndGhfZmlsZSwgY2xvbmVfcHJldmFsZW5jZV9maWxlLCBkYl9wYXRoKQp0cmVlcyA8LSBsYXBwbHkodHJlZV9icmFuY2hfZGF0YSwgZnVuY3Rpb24oeCkgeCR0cmVlKQpwcmV2YWxlbmNlcyA8LSByYmluZC5maWxsKGxhcHBseSh0cmVlX2JyYW5jaF9kYXRhLCBmdW5jdGlvbih4KSB4JHByZXZhbGVuY2VfZGF0KSkKYnJhbmNoX2xlbmd0aHMgPC0gcmJpbmQuZmlsbChsYXBwbHkodHJlZV9icmFuY2hfZGF0YSwgZnVuY3Rpb24oeCkgeCRicmFuY2hfZGF0KSkKCmNjZnMgPC0gY29tcHV0ZV9jY2YocHJldmFsZW5jZXMsIHRyZWVzLCBpZF90eXBlID0gImNvbmRlbnNlZF9pZCIpCmNjZnNfbGFiZWxlZCA8LSBtZXJnZShjY2ZzLCBicmFuY2hfbGVuZ3RocywgYnk9YygibGFiZWwiLCAibm9kZSIsICJwYXRpZW50X2lkIikpCgojIyBOZWVkIG5vbi1ub3JtYWxpemVkIGRpc3RhbmNlcwpjbG9uZV9kaXN0cyA8LSBjbG9uZV9kaXN0YW5jZXMoY2Nmc19sYWJlbGVkLCBub3JtYWxpemUgPSBUUlVFLCBpZF90eXBlID0gImNvbmRlbnNlZF9pZCIpCgpjbG9uZV9kaXN0cyRwYXRpZW50X2lkIDwtIGZhY3RvcihjbG9uZV9kaXN0cyRwYXRpZW50X2lkKQoKY2xvbmVfZGlzdHNfbWVsdGVkIDwtIHJiaW5kLmZpbGwobGFwcGx5KDE6bnJvdyhjbG9uZV9kaXN0cyksIGZ1bmN0aW9uKGkpIHsKICBwYXRpZW50IDwtIGNsb25lX2Rpc3RzJHBhdGllbnRfaWRbaV0KICBkaXN0bWF0IDwtIGNsb25lX2Rpc3RzJGRpc3RfY2xvbmVzX3dlaWdodGVkW1tpXV0KICAKICBkaXN0ZGYgPC0gc2V0TmFtZXMobWVsdChhcy5tYXRyaXgoZGlzdG1hdCkpLCBjKCJpZDEiLCAiaWQyIiwgImNsb25lX2Rpc3QiKSkKICBkaXN0ZGYkaWQxIDwtIGZhY3RvcihkaXN0ZGYkaWQxKQogIGRpc3RkZiRpZDIgPC0gZmFjdG9yKGRpc3RkZiRpZDIpCiAgZGlzdGRmIDwtIHN1YnNldChkaXN0ZGYsIGFzLm51bWVyaWMoaWQxKSA+IGFzLm51bWVyaWMoaWQyKSkKICByZXR1cm4oZGlzdGRmKQp9KSkKYGBgCgpgYGB7cn0KZGYgPC0gbWVyZ2UoY2xvbmVfZGlzdHNfbWVsdGVkLCBzZXROYW1lcyhtb2xzdWJ0eXBlcywgYygiaWQxIiwgInN1YnR5cGUxIikpKQpkZiA8LSBtZXJnZShkZiwgc2V0TmFtZXMobW9sc3VidHlwZXMsIGMoImlkMiIsICJzdWJ0eXBlMiIpKSkKZGYkZXF1YWxfc3VidHlwZXMgPC0gZGYkc3VidHlwZTEgPT0gZGYkc3VidHlwZTIKZGYkcGF0aWVudF9pZCA8LSBtYXBfaWQoZGYkaWQxLCBmcm9tID0gImNvbmRlbnNlZF9pZCIsIHRvID0gInBhdGllbnRfaWQiLCBkYl9wYXRoKQpgYGAKCmBgYHtyfQpnZ3Bsb3QoZGYsIGFlcyh4PWVxdWFsX3N1YnR5cGVzLCB5PWNsb25lX2Rpc3QpKSArIGdlb21fYm94cGxvdChvdXRsaWVyLnNpemUgPSAtMSkgKyBnZW9tX2ppdHRlcihhZXMoY29sb3VyPXBhdGllbnRfaWQpLCBwb3NpdGlvbj1wb3NpdGlvbl9qaXR0ZXIod2lkdGg9MC4yLCBoZWlnaHQ9MCkpICsgdGhlbWVfYncoKSArIHRoZW1lX1B1YmxpY2F0aW9uKCkgKyBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IHBhbF9wYXRpZW50KQpgYGAKCk1ha2luZyB0aGlzIGNvbXBhcmlzb24gYWNyb3NzIHBhdGllbnRzIGlzIHVuZmFpciwgYXMgY2xvbmUgZGlzdGFuY2UgdmFsdWVzIChldmVuIHdoZW4gbm9ybWFsaXplZCkgbWF5IGJlIHZlcnkgZGlmZmVyZW50IGluIHNjYWxlIGZyb20gcGF0aWVudCB0byBwYXRpZW50LiBJdCdzIG9idmlvdXMgdGhlcmUncyBubyBwYXR0ZXJuIEFDUk9TUyBwYXRpZW50cywgYnV0IGhvdyBhYm91dCBXSVRISU4gcGF0aWVudHM/IAoKSWYgd2UgYXNzdW1lIGEgR2F1c3NpYW4gcmVzcG9uc2UgdmFyaWFibGUgKHdoaWNoIGlzIGRlZmluaXRlbHkgbm90IHRydWUpOgoKYGBge3J9CnN1bW1hcnkobG1lcihjbG9uZV9kaXN0IH4gZXF1YWxfc3VidHlwZXMgKyAoMXxwYXRpZW50X2lkKSwgZGYpKQpgYGAKClRoZXJlJ3MgYSB0cmVuZCB0b3dhcmQgbG93ZXIgY2xvbmUgZGlzdGFuY2UgYmV0d2VlbiBwYWlycyBvZiB0dW1vdXJzIHdpdGggZXF1YWwgc3VidHlwZSwgYnV0IHRoaXMgaXMgbm90IHNpZ25pZmljYW50LiAKCldoYXQgaWYgd2UgdXNlIGEgbm9ucGFyYW1ldHJpYyB0ZXN0PyAKCmBgYHtyfQp0bXAgPC0gZGYgJT4lIGdyb3VwX2J5KHBhdGllbnRfaWQpICU+JSBzdW1tYXJpc2UoYm90aF9sZXZlbHMgPSAhUmVkdWNlKCcmJywgZXF1YWxfc3VidHlwZXMpICYgUmVkdWNlKCd8JywgZXF1YWxfc3VidHlwZXMpKQoKZGZfZmlsdGVyZWQgPC0gc3Vic2V0KGRmLCBwYXRpZW50X2lkICVpbiUgc3Vic2V0KHRtcCwgYm90aF9sZXZlbHMpJHBhdGllbnRfaWQpCgpyZXMgPC0gbmVzdGVkUmFua3NUZXN0KGNsb25lX2Rpc3QgfiAhZXF1YWxfc3VidHlwZXMgfCBwYXRpZW50X2lkLCBkYXRhID0gZGZfZmlsdGVyZWQsIG4uaXRlciA9IDUwMDApCnJlcwoKcGxvdChyZXMpCmBgYAoKV2UgZG8gc2VlIGEgdHJlbmQhIFdlIGNhbiBvbmx5IGRvIHRoaXMgZm9yIHBhdGllbnRzIHdpdGggc2FtcGxlcyBpbiBib3RoIGNsdXN0ZXJzLiBTbywgc2FtcGxlcyB3aXRoIGRpZmZlcmVudCBtb2xlY3VsYXIgc3VidHlwZXMgYXJlIGFsc28gbW9yZSBsaWtlbHkgdG8gaGF2ZSBkaWZmZXJlbnQgY2xvbmFsIGNvbXBvc2l0aW9ucy4gQnV0IHRoaXMgZG9lcyBub3QgYXBwZWFyIHRvIGJlIHNpZ25pZmljYW50IHdoZW4gSSBydW4gaXQgd2l0aCB0aGUgbmV3IHN1YnR5cGluZyAoY29tYmluZWQgdXNpbmcgSUNHQyBmb3Igc3VidHlwaW5nIC4uLikuIAoKCg==