library(ithi.utils)
load_base_libs()
library(GenomicRanges)

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

Colour palettes

pal_patient <- select_palette("patient")

Parameters

db_path <- snakemake@params$db
ihc_table_path <- snakemake@input$ihc_table
ihc_table_slide_path <- snakemake@input$ihc_table_slide

xcr_table_path <- snakemake@input$xcr_table
prevalence_option <- snakemake@params$prevalence_option
distance_method <- snakemake@params$xcr_distance_method
filter_tissue <- snakemake@params$xcr_filter_tissue == "filter"
tcr_diversity_file <- snakemake@input$tcr_diversity
bcr_diversity_file <- snakemake@input$bcr_diversity

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

normalize_option <- snakemake@params$normalize %in% c("true", "TRUE", "True")
til_variance_option <- snakemake@params$til_variance_option

Metadata

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

IHC analysis

ihc_table <- fread(ihc_table_path)
ihc_table_slide <- fread(ihc_table_slide_path)
ihc_samples <- ihc_table$condensed_id

if (any(table(ihc_samples) != 1)) {
    stop("Duplicate condensed_ids from the same analysis type (IHC).")
}

Total TIL densities

tiltypes <- c("T_CD8_density", "T_CD4_density", "T_CD20_density", "T_Plasma_density")

plot_ihc_barplot(ihc_table, tiltypes, labs = c("CD8+ T cell", "CD4+ T cell", 
    "CD20+ B cell", "Plasma cell"), Y_MAX = 1500, db_path = db_path)

TIL anatomic differences

tiltypes <- c("T_CD8_density", "T_CD4_density", "T_CD20_density", "T_Plasma_density")
ihc_data <- subset(ihc_table, select = c("condensed_id", "patient_id", tiltypes))
ihc_data$tissue <- extract_tissue(ihc_data$condensed_id, type = "general", shorten = TRUE)

ihc_data_melted <- melt(ihc_data, id.vars = c("condensed_id", "patient_id", 
    "tissue"), measure.vars = tiltypes, variable.name = "Type", value.name = "Density")

RENAME_MAP <- list(T_CD8_density = "CD8+", T_CD4_density = "CD4+", T_CD20_density = "CD20+", 
    T_Plasma_density = "Plasma")

ihc_data_melted$Type <- mapvalues(ihc_data_melted$Type, from = names(RENAME_MAP), 
    to = unname(unlist(RENAME_MAP)))

## Calculate p-values

# We have multiple levels and multiple random effect levels. Residuals are
# ~approx normal.  Let's use a GLM. I don't know of a nonparametric
# alternative for >=3 Tx levels.
pvals <- setNames(ddply(ihc_data_melted, .(Type), function(x) {
    df <- subset(as.data.frame(x), !is.na(Density))
    model <- lme(Density ~ tissue, random = ~1 | patient_id, data = df, method = "REML")
    result <- anova.lme(model, type = "sequential", adjustSigma = FALSE)
    
    tissue_pval <- result$`p-value`[2]
    eq <- substitute(italic(P) == p, list(p = format(tissue_pval, digits = 3)))
    return(as.character(as.expression(eq)))
}), c("Type", "p.value"))
p <- ggplot(ihc_data_melted, aes(x = Type, y = Density)) + geom_boxplot(aes(fill = tissue)) + 
    theme_bw() + theme_Publication() + scale_fill_Publication() + facet_wrap(~Type, 
    scales = "free", ncol = 2) + theme(axis.text.x = element_text(size = 0), 
    legend.text = element_text(size = 6, family = "Helvetica"), axis.text.y = element_text(size = 6, 
        family = "Helvetica"), axis.title = element_text(size = 8, family = "Helvetica")) + 
    geom_text(data = pvals, aes(x = Inf, y = Inf, label = p.value), hjust = 1.1, 
        vjust = 1.5, size = 3, parse = TRUE) + scale_y_continuous(trans = log10_trans(), 
    breaks = c(1, 10, 100, 1000))
p

TIL correlations

ihcmat <- subset(ihc_table, select = c("E_CD8_density", "E_CD4_density", "E_CD20_density", 
    "E_Plasma_density", "S_CD8_density", "S_CD4_density", "S_CD20_density", 
    "S_Plasma_density"))
colnames(ihcmat) <- c("E CD8+", "E CD4+", "E CD20+", "E Plasma", "S CD8+", "S CD4+", 
    "S CD20+", "S Plasma")

corres <- corr.test(ihcmat, adjust = "fdr")
corres.r <- corres$r
corres.p <- corres$p
hc <- hclust(dist(corres.r), method = "ward.D")

diag(corres.r) <- NA
diag(corres.p) <- NA

corres.labels <- format(signif(corres.p, 2), 2)

cols <- colorRampPalette(c("#95cbee", "#ffd73e", "#ce472e"))(100)
par(family = "helvetica")
pheatmap(corres.r[hc$order, hc$order], display_numbers = corres.labels, color = cols, 
    number_color = "white", cluster_rows = FALSE, cluster_cols = FALSE)

TCR/BCR analysis

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

Read 29.5% of 304822 rows
Read 68.9% of 304822 rows
Read 304822 rows and 18 (of 18) columns from 0.070 GB file in 00:00:05
xcr_samples <- unique(xcr_table$condensed_id)
tcr_segment_type <- "TRB"
bcr_segment_type <- "IGH"

id_type <- "condensed_id"

Number of unique VOAs: 95, number of unique condensed_ids: 95.

Clonotype repertoires

Circos

TCR
tcr_clonotypes <- subset(xcr_table, type == tcr_segment_type)

tcr_cross_table <- cross_tabulate(tcr_clonotypes, id_type = id_type)

if (prevalence_option != "freq") {
    summary <- tcr_clonotypes %>% group_by(condensed_id) %>% summarise(nclones = length(cdr3nt))
    dists <- compute_immune_distance_matrix(tcr_cross_table, method = "num_overlaps")
} else {
    summary <- tcr_clonotypes %>% group_by(condensed_id) %>% summarise(nreads = sum(count))
    dists <- compute_immune_distance_matrix(tcr_cross_table, method = "num_reads")
}

overlapmat <- setNames(reshape2::melt(dists), c("sample1", "sample2", "divshared"))
overlapmat <- merge(overlapmat, setNames(summary, c("sample1", "div1")))
overlapmat <- merge(overlapmat, setNames(summary, c("sample2", "div2")))

overlapmat <- subset(overlapmat, sample1 != sample2)
overlapmat$sample1 <- as.character(overlapmat$sample1)
overlapmat$sample2 <- as.character(overlapmat$sample2)

df <- data.frame(sample1 = overlapmat$sample1, sample2 = overlapmat$sample2, 
    shared = overlapmat$divshared)
circos_plot(df, "shared", id_type = id_type, db_path = db_path)

BCR
bcr_clonotypes <- subset(xcr_table, type == bcr_segment_type)

bcr_cross_table <- cross_tabulate(bcr_clonotypes, id_type = id_type)

if (prevalence_option != "freq") {
    summary <- bcr_clonotypes %>% group_by(condensed_id) %>% summarise(nclones = length(cdr3nt))
    dists <- compute_immune_distance_matrix(bcr_cross_table, method = "num_overlaps")
} else {
    summary <- bcr_clonotypes %>% group_by(condensed_id) %>% summarise(nreads = sum(count))
    dists <- compute_immune_distance_matrix(bcr_cross_table, method = "num_reads")
}

overlapmat <- setNames(reshape2::melt(dists), c("sample1", "sample2", "divshared"))
overlapmat <- merge(overlapmat, setNames(summary, c("sample1", "div1")))
overlapmat <- merge(overlapmat, setNames(summary, c("sample2", "div2")))

overlapmat <- subset(overlapmat, sample1 != sample2)
overlapmat$sample1 <- as.character(overlapmat$sample1)
overlapmat$sample2 <- as.character(overlapmat$sample2)

df <- data.frame(sample1 = overlapmat$sample1, sample2 = overlapmat$sample2, 
    shared = overlapmat$divshared)
circos_plot(df, "shared", id_type = id_type, db_path = db_path)

Clonotype repertoire similarity

cross_tables <- list(tcr = tcr_cross_table, bcr = bcr_cross_table)

The distance metric being used is horn.

distance_matrices <- lapply(cross_tables, function(cross_table) {
    distmat <- compute_immune_distance_matrix(cross_table, method = distance_method)
    mat <- as.matrix(distmat)
    return(mat)
})
sims_raw <- lapply(distance_matrices, function(mat) {
    sims <- average_intrapatient_similarity(mat, filter_tissue = filter_tissue, 
        id_type = id_type, db_path = db_path, raw = TRUE)
    sims <- subset(sims, select = -c(patient2))
    colnames(sims) <- mapvalues(colnames(sims), from = c("patient1"), to = c("patient"))
    return(sims)
})

sims_avg <- lapply(distance_matrices, function(mat) {
    average_intrapatient_similarity(mat, filter_tissue = filter_tissue, id_type = id_type, 
        db_path = db_path, raw = FALSE)
})
ignore <- lapply(list("tcr", "bcr"), function(segment) {
    dat <- sims_raw[[segment]]
    p <- ggplot(dat, aes(x = factor(patient), y = 1 - dist)) + geom_boxplot(aes(fill = factor(patient))) + 
        theme_bw() + theme_Publication() + scale_fill_manual(values = pal_patient) + 
        xlab("Patient") + ylab("Pairwise repertoire similarity") + guides(fill = guide_legend(title = "Patient"))
    print(p)
})

Correlation with TIL densities

mean_til_densities <- subset(ihc_table, select = -c(voa, site_id, condensed_id)) %>% 
    group_by(patient_id) %>% summarise_all(function(x) mean(x, na.rm = TRUE))

sims_avg_tcr_til <- merge(sims_avg$tcr, mean_til_densities, by = "patient_id")
sims_avg_bcr_til <- merge(sims_avg$bcr, mean_til_densities, by = "patient_id")

sims_avg_tcr_til$patient_id <- factor(sims_avg_tcr_til$patient_id)
sims_avg_bcr_til$patient_id <- factor(sims_avg_bcr_til$patient_id)

sims_avg_tcr_til <- subset(sims_avg_tcr_til, ncomparisons >= 6)
sims_avg_bcr_til <- subset(sims_avg_bcr_til, ncomparisons >= 6)
tiltypenames <- colnames(ihc_table)[str_detect(colnames(ihc_table), "^(T).*_density$")]

sims_avg_melted <- lapply(list(tcr = sims_avg_tcr_til, bcr = sims_avg_bcr_til), 
    function(merged_sims) {
        other_cols <- colnames(merged_sims)[!colnames(merged_sims) %in% tiltypenames]
        melt(merged_sims, id.vars = other_cols, measure.vars = tiltypenames, 
            variable.name = "Type", value.name = "value")
    })

pvals <- lapply(sims_avg_melted, function(merged_sims_melt) {
    setNames(ddply(merged_sims_melt, .(Type), function(x) {
        df <- subset(as.data.frame(x), !is.na(value))
        pval <- with(df, cor.test(median_sim, value, method = "spearman")$p.value)
        # eq <- substitute(italic(P)==p, list(p=format(pval, digits=3)))
        # return(as.character(as.expression(eq)))
        return(pval)
    }), c("Type", "p.value"))
})

pvals <- lapply(pvals, function(x) {
    x$p.value <- p.adjust(x$p.value, method = "fdr")
    x$p.value <- unlist(sapply(x$p.value, function(pval) as.character(as.expression(substitute(italic(P) == 
        p, list(p = format(pval, digits = 3)))))))
    return(x)
})

print(pvals$tcr)
                 Type                p.value
1       T_CD8_density italic(P) == "0.00704"
2       T_CD4_density   italic(P) == "0.142"
3      T_CD20_density   italic(P) == "0.275"
4    T_Plasma_density   italic(P) == "0.449"
5 T_Nonplasma_density   italic(P) == "0.142"
print(pvals$bcr)
                 Type              p.value
1       T_CD8_density italic(P) == "0.383"
2       T_CD4_density italic(P) == "0.383"
3      T_CD20_density italic(P) == "0.383"
4    T_Plasma_density italic(P) == "0.383"
5 T_Nonplasma_density italic(P) == "0.383"
ggplot(sims_avg_tcr_til, aes(x = median_sim, y = T_CD8_density)) + geom_point(aes(colour = patient_id)) + 
    theme_bw() + theme_Publication() + scale_color_manual(values = pal_patient) + 
    annotate(geom = "text", label = subset(pvals$tcr, Type == "T_CD8_density")$p.value, 
        x = Inf, y = Inf, hjust = 1, vjust = 1, parse = TRUE) + scale_y_log10()

ggplot(sims_avg_tcr_til, aes(x = median_sim, y = T_CD4_density)) + geom_point(aes(colour = patient_id)) + 
    theme_bw() + theme_Publication() + scale_color_manual(values = pal_patient) + 
    annotate(geom = "text", label = subset(pvals$tcr, Type == "T_CD4_density")$p.value, 
        x = Inf, y = Inf, hjust = 1, vjust = 1, parse = TRUE) + scale_y_log10()

ggplot(sims_avg_bcr_til, aes(x = median_sim, y = T_CD20_density)) + geom_point(aes(colour = patient_id)) + 
    theme_bw() + theme_Publication() + scale_color_manual(values = pal_patient) + 
    annotate(geom = "text", label = subset(pvals$bcr, Type == "T_CD20_density")$p.value, 
        x = Inf, y = Inf, hjust = 1, vjust = 1, parse = TRUE) + scale_y_log10()

ggplot(sims_avg_bcr_til, aes(x = median_sim, y = T_Plasma_density)) + geom_point(aes(colour = patient_id)) + 
    theme_bw() + theme_Publication() + scale_color_manual(values = pal_patient) + 
    annotate(geom = "text", label = subset(pvals$bcr, Type == "T_Plasma_density")$p.value, 
        x = Inf, y = Inf, hjust = 1, vjust = 1, parse = TRUE) + scale_y_log10()

Perhaps a better way of doing this is to use generalized linear models?

summary(glm(median_sim ~ T_CD8_density + T_CD4_density + T_CD20_density + T_Plasma_density, 
    data = sims_avg_tcr_til))

Call:
glm(formula = median_sim ~ T_CD8_density + T_CD4_density + T_CD20_density + 
    T_Plasma_density, data = sims_avg_tcr_til)

Deviance Residuals: 
     Min        1Q    Median        3Q       Max  
-0.17954  -0.12617  -0.09175   0.06449   0.41226  

Coefficients:
                   Estimate Std. Error t value Pr(>|t|)  
(Intercept)       0.1766786  0.1120449   1.577   0.1408  
T_CD8_density     0.0019203  0.0008616   2.229   0.0457 *
T_CD4_density    -0.0013891  0.0025360  -0.548   0.5939  
T_CD20_density    0.0044570  0.0077225   0.577   0.5745  
T_Plasma_density -0.0026832  0.0032726  -0.820   0.4283  
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

(Dispersion parameter for gaussian family taken to be 0.04466831)

    Null deviance: 0.89980  on 16  degrees of freedom
Residual deviance: 0.53602  on 12  degrees of freedom
AIC: 1.4783

Number of Fisher Scoring iterations: 2
summary(glm(median_sim ~ T_CD8_density + T_CD4_density + T_CD20_density + T_Plasma_density, 
    data = sims_avg_bcr_til))

Call:
glm(formula = median_sim ~ T_CD8_density + T_CD4_density + T_CD20_density + 
    T_Plasma_density, data = sims_avg_bcr_til)

Deviance Residuals: 
      Min         1Q     Median         3Q        Max  
-0.169546  -0.084801   0.002978   0.051342   0.262292  

Coefficients:
                   Estimate Std. Error t value Pr(>|t|)   
(Intercept)       0.1645175  0.0662052   2.485  0.02870 * 
T_CD8_density     0.0009342  0.0005091   1.835  0.09140 . 
T_CD4_density    -0.0001514  0.0014985  -0.101  0.92119   
T_CD20_density    0.0074466  0.0045631   1.632  0.12864   
T_Plasma_density -0.0060288  0.0019337  -3.118  0.00889 **
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

(Dispersion parameter for gaussian family taken to be 0.01559549)

    Null deviance: 0.37101  on 16  degrees of freedom
Residual deviance: 0.18715  on 12  degrees of freedom
AIC: -16.41

Number of Fisher Scoring iterations: 2

Summary stats

Diversity

richness_column <- "observedDiversity_mean"
shannon_column <- "shannonWienerIndex_mean"
inverse_simpson_column <- "inverseSimpsonIndex_mean"
diversity_files <- list(tcr = tcr_diversity_file, bcr = bcr_diversity_file)

diversity <- lapply(diversity_files, function(f) {
    read_xcr_diversity_file(f, db_path)
})

plot_diversity <- function(col) {
    ignore <- lapply(diversity, function(df) {
        p <- ggplot(df, aes_string(x = "patient_id", y = col)) + geom_boxplot(aes(fill = patient_id)) + 
            theme_bw() + theme_Publication() + scale_fill_manual(values = pal_patient) + 
            xlab("Patient") + ylab("Diversity")
        print(p)
    })
}
Richness
plot_diversity(richness_column)

Shannon entropy
plot_diversity(shannon_column)

Inverse Simpson
plot_diversity(inverse_simpson_column)

Nanostring

exprs <- fread(nanostring_data_path)
labels <- fread(nanostring_annotations_path)
exprmat <- as.data.frame(getExprs(exprs))
annomat <- getAnnos(exprs)
rownames(exprmat) <- annomat$Name

nanostring_samples <- map_id(colnames(exprmat), from = "voa", to = "condensed_id", 
    db_path = db_path)
if (any(table(nanostring_samples) != 1)) {
    stop("Duplicate condensed_ids from the same analysis type (Nanostring).")
}

Cell-type expression signatures

patient_strings <- as.character(map_id(colnames(exprmat), from = "voa", to = "patient_id", 
    db_path = db_path))

## There are no duplicate condensed_ids when mapping from FFPE voa's, so we
## can convert here for readability
colnames(exprmat) <- nanostring_samples

annotations <- merge(annomat, labels, by.x = "Name", by.y = "Gene")
patients <- data.frame(Patient = patient_strings)
rownames(patients) <- colnames(exprmat)

cell_types <- subset(annotations, cell_type != "")$cell_type
cell_type_genes <- subset(annotations, cell_type != "")$Name[order(cell_types)]

annorow <- as.data.frame(subset(annotations, select = c(cell_type)))
rownames(annorow) <- annotations$Name
colnames(annorow)[1] <- "Cell Type"

annocol <- patients

annocolors <- list(`Cell Type` = get_colour_palette(unique(cell_types)), Patient = select_palette("patient"))

mat <- clip_values(t(scale(t(exprmat[cell_type_genes, ]))), hi = 2, lo = -2)

gaps <- which(rev(!duplicated(rev(cell_types[order(cell_types)]))))
pheatmap(mat, annotation_row = annorow, annotation_col = annocol, annotation_colors = annocolors, 
    cluster_rows = FALSE, show_colnames = FALSE, clustering_method = "ward.D", 
    gaps_row = gaps)

Cell-type metagenes

celltype_matrix <- create_celltype_matrix(exprs, labels, db_path)
celltype_matrix_scaled <- clip_values(t(scale(t(celltype_matrix))), hi = 2, 
    lo = -2)

pheatmap(celltype_matrix_scaled, annotation_col = annocol, annotation_colors = annocolors, 
    cluster_rows = TRUE, show_colnames = FALSE, clustering_method = "ward.D")

Correlation with intrapatient XCR variability

celltype_df <- data.frame(celltype_matrix %>% t, check.names = FALSE) %>% rownames_to_column(var = "condensed_id")
celltype_df$patient_id <- map_id(celltype_df$condensed_id, from = "condensed_id", 
    to = "patient_id", db_path)

mean_celltype_expression <- subset(celltype_df, select = -c(condensed_id)) %>% 
    group_by(patient_id) %>% summarise_all(function(x) mean(x, na.rm = TRUE))

sims_avg_tcr_expr <- merge(sims_avg$tcr, mean_celltype_expression, by = "patient_id")
sims_avg_bcr_expr <- merge(sims_avg$bcr, mean_celltype_expression, by = "patient_id")

sims_avg_tcr_expr$patient_id <- factor(sims_avg_tcr_expr$patient_id)
sims_avg_bcr_expr$patient_id <- factor(sims_avg_bcr_expr$patient_id)
exprnames <- colnames(celltype_df)[!colnames(celltype_df) %in% c("condensed_id", 
    "patient_id")]

sims_avg_melted <- lapply(list(tcr = sims_avg_tcr_expr, bcr = sims_avg_bcr_expr), 
    function(merged_sims) {
        other_cols <- colnames(merged_sims)[!colnames(merged_sims) %in% exprnames]
        melt(merged_sims, id.vars = other_cols, measure.vars = exprnames, variable.name = "Type", 
            value.name = "value")
    })

pvals <- lapply(sims_avg_melted, function(merged_sims_melt) {
    setNames(ddply(merged_sims_melt, .(Type), function(x) {
        df <- subset(as.data.frame(x), !is.na(value))
        pval <- with(df, cor.test(mean_sim, value, method = "spearman")$p.value)
        eq <- substitute(italic(P) == p, list(p = format(pval, digits = 3)))
        return(as.character(as.expression(eq)))
    }), c("Type", "p.value"))
})

print(pvals$tcr)
                 Type                 p.value
1                 aDC   italic(P) == "0.0138"
2              B-cell    italic(P) == "0.177"
3          CD8 T-cell  italic(P) == "0.00941"
4      Cytotoxic cell italic(P) == "0.000607"
5                  DC    italic(P) == "0.992"
6         Eosinophils   italic(P) == "0.0111"
7                 iDC    italic(P) == "0.199"
8         Macrophages    italic(P) == "0.211"
9           Mast cell   italic(P) == "0.0598"
10        Neutrophils    italic(P) == "0.368"
11 NK CD56bright cell    italic(P) == "0.042"
12    NK CD56dim cell  italic(P) == "0.00021"
13            NK cell    italic(P) == "0.219"
14                pDC    italic(P) == "0.807"
15      T helper cell    italic(P) == "0.677"
16             T-cell italic(P) == "0.000655"
17                Tcm  italic(P) == "0.00029"
18                Tem    italic(P) == "0.538"
19                TFH  italic(P) == "0.00151"
20                Tgd    italic(P) == "0.617"
21           Th1 cell   italic(P) == "0.0132"
22          Th17 cell    italic(P) == "0.347"
23           Th2 cell    italic(P) == "0.967"
24               Treg    italic(P) == "0.368"
print(pvals$bcr)
                 Type               p.value
1                 aDC  italic(P) == "0.202"
2              B-cell italic(P) == "0.0442"
3          CD8 T-cell  italic(P) == "0.237"
4      Cytotoxic cell  italic(P) == "0.199"
5                  DC   italic(P) == "0.46"
6         Eosinophils  italic(P) == "0.695"
7                 iDC  italic(P) == "0.183"
8         Macrophages  italic(P) == "0.649"
9           Mast cell italic(P) == "0.0712"
10        Neutrophils italic(P) == "0.0197"
11 NK CD56bright cell  italic(P) == "0.916"
12    NK CD56dim cell  italic(P) == "0.129"
13            NK cell  italic(P) == "0.846"
14                pDC  italic(P) == "0.382"
15      T helper cell  italic(P) == "0.972"
16             T-cell  italic(P) == "0.237"
17                Tcm  italic(P) == "0.129"
18                Tem  italic(P) == "0.767"
19                TFH  italic(P) == "0.239"
20                Tgd  italic(P) == "0.337"
21           Th1 cell  italic(P) == "0.429"
22          Th17 cell italic(P) == "0.0524"
23           Th2 cell italic(P) == "0.0757"
24               Treg  italic(P) == "0.382"
ggplot(sims_avg_tcr_expr, aes(x = mean_sim, y = `CD8 T-cell`)) + geom_point(aes(colour = patient_id)) + 
    theme_bw() + theme_Publication() + scale_color_manual(values = pal_patient) + 
    annotate(geom = "text", label = subset(pvals$tcr, Type == "CD8 T-cell")$p.value, 
        x = Inf, y = Inf, hjust = 1, vjust = 1, parse = TRUE)

ggplot(sims_avg_tcr_expr, aes(x = mean_sim, y = `T helper cell`)) + geom_point(aes(colour = patient_id)) + 
    theme_bw() + theme_Publication() + scale_color_manual(values = pal_patient) + 
    annotate(geom = "text", label = subset(pvals$tcr, Type == "T helper cell")$p.value, 
        x = Inf, y = Inf, hjust = 1, vjust = 1, parse = TRUE)

ggplot(sims_avg_tcr_expr, aes(x = mean_sim, y = `T-cell`)) + geom_point(aes(colour = patient_id)) + 
    theme_bw() + theme_Publication() + scale_color_manual(values = pal_patient) + 
    annotate(geom = "text", label = subset(pvals$tcr, Type == "T-cell")$p.value, 
        x = Inf, y = Inf, hjust = 1, vjust = 1, parse = TRUE)

ggplot(sims_avg_tcr_expr, aes(x = mean_sim, y = `Th1 cell`)) + geom_point(aes(colour = patient_id)) + 
    theme_bw() + theme_Publication() + scale_color_manual(values = pal_patient) + 
    annotate(geom = "text", label = subset(pvals$tcr, Type == "Th1 cell")$p.value, 
        x = Inf, y = Inf, hjust = 1, vjust = 1, parse = TRUE)

ggplot(sims_avg_tcr_expr, aes(x = mean_sim, y = `Th2 cell`)) + geom_point(aes(colour = patient_id)) + 
    theme_bw() + theme_Publication() + scale_color_manual(values = pal_patient) + 
    annotate(geom = "text", label = subset(pvals$tcr, Type == "Th2 cell")$p.value, 
        x = Inf, y = Inf, hjust = 1, vjust = 1, parse = TRUE)

ggplot(sims_avg_bcr_expr, aes(x = mean_sim, y = `B-cell`)) + geom_point(aes(colour = patient_id)) + 
    theme_bw() + theme_Publication() + scale_color_manual(values = pal_patient) + 
    annotate(geom = "text", label = subset(pvals$bcr, Type == "B-cell")$p.value, 
        x = Inf, y = Inf, hjust = 1, vjust = 1, parse = TRUE)

Intrapatient variability (overall)

Requires that condensed_ids are never duplicated in any analysis type. I’ve inserted checks into each of the sections above to ensure this.

Nanostring

We’ll work with cell-type metagenes as working with all expression values will be biased by the number of genes associated with each cell type.

TIL densities

tiltypes <- c("T_CD8_density", "T_CD4_density", "T_CD20_density", "T_Plasma_density")

ihc_slide_data <- subset(ihc_table_slide, select = c("condensed_id", "patient_id", 
    "slide", tiltypes))
ihc_melted <- melt(ihc_slide_data, id.vars = c("condensed_id", "patient_id", 
    "slide"), measure.vars = tiltypes, variable.name = "tiltype", value.name = "density")
ihc_melted$tiltype <- as.character(ihc_melted$tiltype)

XCR

Correlations

T_CELL_TYPES <- c("T_CD8_density", "T_CD4_density")
B_CELL_TYPES <- c("T_CD20_density", "T_Plasma_density")
NANOSTRING_T_TYPES <- c("CD8 T-cell", "Cytotoxic cell", "T-cell", "T helper cell", 
    "Th1 cell", "Th2 cell", "Th17 cell", "Treg", "Tcm", "Tem")
NANOSTRING_B_TYPES <- c("B-cell", "TFH")
XCR_T_TYPES <- c("tcr")
XCR_B_TYPES <- c("bcr")

cols <- colorRampPalette(c("#95cbee", "#ffd73e", "#ce472e"))(100)

corplot <- function(var1, var2, sub1, sub2) {
    cor_df <- Reduce(function(x, y) merge(x, y, by = "patient_id"), list(var1, 
        var2))
    cor_matrix <- subset(cor_df, select = -c(patient_id))
    
    corres <- corr.test(cor_matrix, method = "pearson", adjust = "none")
    # hc <- hclust(dist(corres$r), method='ward.D')
    diag(corres$r) <- NA
    diag(corres$p) <- NA
    
    corres.r <- corres$r[sub1, sub2]
    corres.p <- corres$p[sub1, sub2]
    
    corres.p.labels <- format(signif(corres.p, 2), 2)
    
    pheatmap(corres.r, display_numbers = corres.p.labels, color = cols, number_color = "white", 
        cluster_rows = TRUE, cluster_cols = TRUE)
}

TIL densities vs. XCR

intersect_samples <- compute_sample_intersection(samples_list = list(ihc_samples, 
    xcr_samples))

til_var <- compute_ihc_var(ihc_melted, ihc_table, samples = intersect_samples, 
    til_variance_option = "stabilize", tiltypes)
xcr_var <- compute_xcr_var(distance_matrices, samples = intersect_samples, filter_tissue = FALSE, 
    id_type = "condensed_id", db_path)

ihc_entries <- c(T_CELL_TYPES, B_CELL_TYPES)
xcr_entries <- c(XCR_T_TYPES, XCR_B_TYPES)

corplot(til_var, xcr_var, ihc_entries, xcr_entries)

TIL densities vs. Nanostring

intersect_samples <- compute_sample_intersection(samples_list = list(ihc_samples, 
    nanostring_samples))

til_var <- compute_ihc_var(ihc_melted, ihc_table, samples = intersect_samples, 
    til_variance_option = "stabilize", tiltypes)
expr_var <- compute_expr_var(celltype_matrix, samples = intersect_samples, scale = TRUE)

ihc_entries <- c(T_CELL_TYPES, B_CELL_TYPES)
expr_entries <- c(NANOSTRING_T_TYPES, NANOSTRING_B_TYPES)

corplot(til_var, expr_var, ihc_entries, expr_entries)

XCR vs. Nanostring

intersect_samples <- compute_sample_intersection(samples_list = list(xcr_samples, 
    nanostring_samples))

xcr_var <- compute_xcr_var(distance_matrices, samples = intersect_samples, filter_tissue = FALSE, 
    id_type = "condensed_id", db_path)
expr_var <- compute_expr_var(celltype_matrix, samples = intersect_samples, scale = TRUE)

xcr_entries <- c(XCR_T_TYPES, XCR_B_TYPES)
expr_entries <- c(NANOSTRING_T_TYPES, NANOSTRING_B_TYPES)

corplot(xcr_var, expr_var, xcr_entries, expr_entries)

Intrapatient variability (pairwise)

We can also look at consistency in intrapatient similarity on a pairwise basis.

scalar_dist_method <- "manhattan"

ihc_stabilized <- stabilize_ihc_variances(ihc_slide_data, ihc_table, tiltypes)
ihc_stabilized$condensed_id <- factor_id(ihc_stabilized$condensed_id, "condensed_id", 
    db_path)
ihc_sims <- pairwise_similarity_frame(ihc_stabilized, id_col = id_type, group = "patient_id", 
    method = scalar_dist_method)

## always filter_tissue = FALSE
xcr_sims_raw_all <- lapply(names(distance_matrices), function(segment) {
    mat <- distance_matrices[[segment]]
    sims <- average_intrapatient_similarity(mat, filter_tissue = FALSE, id_type = id_type, 
        db_path = db_path, raw = TRUE)
    sims <- subset(sims, select = -c(patient2))
    colnames(sims) <- mapvalues(colnames(sims), from = c("patient1", "dist"), 
        to = c("patient_id", segment))
    return(sims)
})
names(xcr_sims_raw_all) <- names(distance_matrices)

celltype_df <- rownames_to_column(as.data.frame(t(celltype_matrix)), var = id_type)
celltype_df$patient_id <- map_id(celltype_df$condensed_id, from = "condensed_id", 
    to = "patient_id", db_path)
celltype_sims <- pairwise_similarity_frame(celltype_df, id_col = id_type, group = "patient_id", 
    method = scalar_dist_method)

TIL densities vs XCR

ihc_xcr_sims <- Reduce(plyr::join, c(xcr_sims_raw_all, list(ihc_sims)))

ihc_entries <- c(T_CELL_TYPES, B_CELL_TYPES)
xcr_entries <- c(XCR_T_TYPES, XCR_B_TYPES)

cor_matrix <- subset(ihc_xcr_sims, select = c(ihc_entries, xcr_entries))

corres <- corr.test(cor_matrix, method = "spearman", adjust = "none")
# hc <- hclust(dist(corres$r), method='ward.D')
diag(corres$r) <- NA
diag(corres$p) <- NA

corres.r <- corres$r[ihc_entries, xcr_entries]
corres.p <- corres$p[ihc_entries, xcr_entries]

corres.p.labels <- format(signif(corres.p, 2), 2)

pheatmap(corres.r, display_numbers = corres.p.labels, color = cols, number_color = "white", 
    cluster_rows = TRUE, cluster_cols = TRUE)

xcr_sims_melted <- Reduce(plyr::join, xcr_sims_raw_all)

xcr_sims_melted <- melt(xcr_sims_melted, id.vars = colnames(xcr_sims_melted)[!colnames(xcr_sims_melted) %in% 
    xcr_entries], measure.vars = xcr_entries, variable.name = "segment", value.name = "xcrsim")

ihc_sims_melted <- melt(ihc_sims, id.vars = colnames(ihc_sims)[!colnames(ihc_sims) %in% 
    ihc_entries], measure.vars = ihc_entries, variable.name = "tiltype", value.name = "ihcsim")

ihc_xcr_melted <- merge(ihc_sims_melted, xcr_sims_melted, by = c("sample1", 
    "sample2", "patient_id"))

ggplot(ihc_xcr_melted, aes(x = ihcsim, y = xcrsim)) + geom_point(aes(colour = factor(patient_id))) + 
    facet_wrap(tiltype ~ segment, scales = "free") + theme_bw() + theme_Publication() + 
    scale_colour_manual(values = pal_patient)

summary(lmer(xcrsim ~ ihcsim + (1 | patient_id), data = subset(ihc_xcr_melted, 
    tiltype == "T_CD8_density" & segment == "tcr" & !is.na(ihcsim) & !is.na(xcrsim))))
Linear mixed model fit by REML t-tests use Satterthwaite approximations
  to degrees of freedom [lmerMod]
Formula: xcrsim ~ ihcsim + (1 | patient_id)
   Data: subset(ihc_xcr_melted, tiltype == "T_CD8_density" & segment ==  
    "tcr" & !is.na(ihcsim) & !is.na(xcrsim))

REML criterion at convergence: -87.7

Scaled residuals: 
    Min      1Q  Median      3Q     Max 
-4.4652 -0.4055  0.0996  0.4997  1.5398 

Random effects:
 Groups     Name        Variance Std.Dev.
 patient_id (Intercept) 0.05215  0.2284  
 Residual               0.01781  0.1334  
Number of obs: 121, groups:  patient_id, 17

Fixed effects:
             Estimate Std. Error        df t value Pr(>|t|)    
(Intercept) 6.911e-01  5.951e-02 1.853e+01  11.613 6.07e-10 ***
ihcsim      7.561e-03  4.369e-02 1.116e+02   0.173    0.863    
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Correlation of Fixed Effects:
       (Intr)
ihcsim -0.271

Critically, TCR similarity does not correlate with CD8+ or CD4+ T-cell density similarity.

TIL densities vs. Nanostring

ihc_expr_sims <- Reduce(plyr::join, list(ihc_sims, celltype_sims))

ihc_entries <- c(T_CELL_TYPES, B_CELL_TYPES)
expr_entries <- c(NANOSTRING_T_TYPES, NANOSTRING_B_TYPES)

cor_matrix <- subset(ihc_expr_sims, select = c(ihc_entries, expr_entries))

corres <- corr.test(cor_matrix, method = "spearman", adjust = "none")
# hc <- hclust(dist(corres$r), method='ward.D')
diag(corres$r) <- NA
diag(corres$p) <- NA

corres.r <- corres$r[ihc_entries, expr_entries]
corres.p <- corres$p[ihc_entries, expr_entries]

corres.p.labels <- format(signif(corres.p, 2), 2)

pheatmap(corres.r, display_numbers = corres.p.labels, color = cols, number_color = "white", 
    cluster_rows = TRUE, cluster_cols = TRUE)

XCR vs. Nanostring

xcr_expr_sims <- Reduce(plyr::join, c(xcr_sims_raw_all, list(celltype_sims)))

xcr_entries <- c(XCR_T_TYPES, XCR_B_TYPES)
expr_entries <- c(NANOSTRING_T_TYPES, NANOSTRING_B_TYPES)

cor_matrix <- subset(xcr_expr_sims, select = c(xcr_entries, expr_entries))

corres <- corr.test(cor_matrix, method = "spearman", adjust = "none")
# hc <- hclust(dist(corres$r), method='ward.D')
diag(corres$r) <- NA
diag(corres$p) <- NA

corres.r <- corres$r[xcr_entries, expr_entries]
corres.p <- corres$p[xcr_entries, expr_entries]

corres.p.labels <- format(signif(corres.p, 2), 2)

pheatmap(corres.r, display_numbers = corres.p.labels, color = cols, number_color = "white", 
    cluster_rows = TRUE, cluster_cols = TRUE)

These results aren’t really consistent with the patient-level results. It’s important to note, however, that these correlations are calculated naive to the patient labels. i.e. there’s no controlling for patient with a random intercept and slope, etc.

Things become more difficult to interpret if we incorporate random intercepts and slopes. There could be high correlations within patients but low correlations between patients, and vice versa.

Think about how we can show this in a statistically robust way.

IHC pairwise significance testing

pairwise_results_filtered <- ihc_pairwise_significance(ihc_table_slide, tiltypes, 
    db_path, slide_count_filter = 10)
Q_VALUE_THRESHOLD <- 0.01

pairwise_results_filtered$significant <- pairwise_results_filtered$q.value < 
    Q_VALUE_THRESHOLD

pairwise_tiltype_summary <- pairwise_results_filtered %>% group_by(patient_id, 
    tiltype) %>% summarise(any_significant = any(significant))

pairwise_tiltype_count <- pairwise_tiltype_summary %>% group_by(tiltype) %>% 
    summarise(npatients = sum(any_significant, na.rm = TRUE))

pairwise_tiltype_count
x <- pairwise_tiltype_summary %>% group_by(tiltype) %>% do(sigvec = .$any_significant)
x_pairs <- as_tibble(merge(x, x, by = c()))
x_pairs_p <- x_pairs %>% group_by(tiltype.x, tiltype.y) %>% summarise(p.value = pchisq(GenomicRanges::phicoef(table(unlist(sigvec.x), 
    unlist(sigvec.y)))^2 * sum(table(unlist(sigvec.x), unlist(sigvec.y))), df = 1, 
    lower.tail = FALSE))
x_pairs_p$q.value <- p.adjust(x_pairs_p$p.value, method = "fdr")

x_pairs_p
LS0tCnRpdGxlOiAiSW1tdW5lIHZhcmlhYmlsaXR5IgpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHRydWUKICAgIHRvY19kZXB0aDogNQogICAgdG9jX2Zsb2F0OiB0cnVlCnBhcmFtczoKICBybWQ6ICJpbW11bmVfdmFyaWFiaWxpdHkuUm1kIgotLS0KICAgICAgICAgICAgICAgICAgICAgICAgYGBge3IsIGVjaG89RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CgojIyMjIyMjIyBTbmFrZW1ha2UgaGVhZGVyICMjIyMjIyMjCmxpYnJhcnkobWV0aG9kcykKU25ha2VtYWtlIDwtIHNldENsYXNzKAogICAgIlNuYWtlbWFrZSIsCiAgICBzbG90cyA9IGMoCiAgICAgICAgaW5wdXQgPSAibGlzdCIsCiAgICAgICAgb3V0cHV0ID0gImxpc3QiLAogICAgICAgIHBhcmFtcyA9ICJsaXN0IiwKICAgICAgICB3aWxkY2FyZHMgPSAibGlzdCIsCiAgICAgICAgdGhyZWFkcyA9ICJudW1lcmljIiwKICAgICAgICBsb2cgPSAibGlzdCIsCiAgICAgICAgcmVzb3VyY2VzID0gImxpc3QiLAogICAgICAgIGNvbmZpZyA9ICJsaXN0IiwKICAgICAgICBydWxlID0gImNoYXJhY3RlciIKICAgICkKKQpzbmFrZW1ha2UgPC0gU25ha2VtYWtlKAogICAgaW5wdXQgPSBsaXN0KCcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcmVzdWx0cy9uYW5vc3RyaW5nX3Jlc3VsdHMvaXRoX2Z1bGwvcWMvbGltbWFfcXVhbnRpbGUvbm9ybWFsaXplZF9leHByZXNzaW9uX3ZvYV9sYWJlbHNfZmlsdGVyZWQudHN2JywgJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9kYXRhL2V4cHJlc3Npb24vbmFub3N0cmluZy9wYW5jYW5jZXJfYW5ub3RhdGlvbnMudHN2JywgJy9zaGFobGFiL2FsemhhbmcvcGlwZWxpbmVfb3V0cHV0cy9pdGhfaW1tdW5lL21peGNyL21peGNyX3J1bnMvaXRoXzFfMl8zL21peGNyNS9wb3N0cHJvY2Vzcy9UUkIvcG9zdGZpbHRlcl9kaXZlcnNpdHlfc3RhdHMvZGl2ZXJzaXR5LnN0cmljdC5yZXNhbXBsZWQudHh0JywgJy9zaGFobGFiL2FsemhhbmcvcGlwZWxpbmVfb3V0cHV0cy9pdGhfaW1tdW5lL21peGNyL21peGNyX3J1bnMvaXRoXzFfMl8zL21peGNyNS9wb3N0cHJvY2Vzcy9JR0gvcG9zdGZpbHRlcl9kaXZlcnNpdHlfc3RhdHMvZGl2ZXJzaXR5LnN0cmljdC5yZXNhbXBsZWQudHh0JywgJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9yZXN1bHRzL3RhYmxlcy9ydW4yL2loY190YWJsZS50c3YnLCAnUm1kL2ltbXVuZV92YXJpYWJpbGl0eS5SbWQnLCAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3BhcGVyL3Jlc3VsdHMvdGFibGVzL3J1bjIvaWhjX3RhYmxlX3NsaWRlLnRzdicsICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvYW5hbHlzaXMvUm1kL19zaXRlLnltbCcsICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy90YWJsZXMvcnVuMi94Y3JfdGFibGUudHN2JywgIm5hbm9zdHJpbmdfZGF0YSIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3Jlc3VsdHMvbmFub3N0cmluZ19yZXN1bHRzL2l0aF9mdWxsL3FjL2xpbW1hX3F1YW50aWxlL25vcm1hbGl6ZWRfZXhwcmVzc2lvbl92b2FfbGFiZWxzX2ZpbHRlcmVkLnRzdicsICJuYW5vc3RyaW5nX2Fubm90YXRpb25zIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvZGF0YS9leHByZXNzaW9uL25hbm9zdHJpbmcvcGFuY2FuY2VyX2Fubm90YXRpb25zLnRzdicsICJ0Y3JfZGl2ZXJzaXR5IiA9ICcvc2hhaGxhYi9hbHpoYW5nL3BpcGVsaW5lX291dHB1dHMvaXRoX2ltbXVuZS9taXhjci9taXhjcl9ydW5zL2l0aF8xXzJfMy9taXhjcjUvcG9zdHByb2Nlc3MvVFJCL3Bvc3RmaWx0ZXJfZGl2ZXJzaXR5X3N0YXRzL2RpdmVyc2l0eS5zdHJpY3QucmVzYW1wbGVkLnR4dCcsICJiY3JfZGl2ZXJzaXR5IiA9ICcvc2hhaGxhYi9hbHpoYW5nL3BpcGVsaW5lX291dHB1dHMvaXRoX2ltbXVuZS9taXhjci9taXhjcl9ydW5zL2l0aF8xXzJfMy9taXhjcjUvcG9zdHByb2Nlc3MvSUdIL3Bvc3RmaWx0ZXJfZGl2ZXJzaXR5X3N0YXRzL2RpdmVyc2l0eS5zdHJpY3QucmVzYW1wbGVkLnR4dCcsICJpaGNfdGFibGUiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9yZXN1bHRzL3RhYmxlcy9ydW4yL2loY190YWJsZS50c3YnLCAibm90ZWJvb2siID0gJ1JtZC9pbW11bmVfdmFyaWFiaWxpdHkuUm1kJywgImloY190YWJsZV9zbGlkZSIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3BhcGVyL3Jlc3VsdHMvdGFibGVzL3J1bjIvaWhjX3RhYmxlX3NsaWRlLnRzdicsICJzaXRlX2ZpbGUiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9hbmFseXNpcy9SbWQvX3NpdGUueW1sJywgInhjcl90YWJsZSIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3BhcGVyL3Jlc3VsdHMvdGFibGVzL3J1bjIveGNyX3RhYmxlLnRzdicpLAogICAgb3V0cHV0ID0gbGlzdCgnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3BhcGVyL3Jlc3VsdHMvd2ViL2ltbXVuZV92YXJpYWJpbGl0eS5uYi5odG1sJyksCiAgICBwYXJhbXMgPSBsaXN0KCdjbG9uZXMnLCAnaG9ybicsICdzdGFiaWxpemUnLCAnaXRoaS1hbmFseXNpcy1pbW11bmUtdmFyaWFiaWxpdHknLCAnbm9maWx0ZXInLCAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL21ldGFkYXRhL2RiL2ltbXVuZV9wcm9qZWN0LnNxbGl0ZTMnLCAnVFJVRScsICJwcmV2YWxlbmNlX29wdGlvbiIgPSAnY2xvbmVzJywgImRiIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvbWV0YWRhdGEvZGIvaW1tdW5lX3Byb2plY3Quc3FsaXRlMycsICJ0aWxfdmFyaWFuY2Vfb3B0aW9uIiA9ICdzdGFiaWxpemUnLCAibmFtZSIgPSAnaXRoaS1hbmFseXNpcy1pbW11bmUtdmFyaWFiaWxpdHknLCAieGNyX2ZpbHRlcl90aXNzdWUiID0gJ25vZmlsdGVyJywgInhjcl9kaXN0YW5jZV9tZXRob2QiID0gJ2hvcm4nLCAibm9ybWFsaXplX29wdGlvbiIgPSAnVFJVRScpLAogICAgd2lsZGNhcmRzID0gbGlzdCgpLAogICAgdGhyZWFkcyA9IDEsCiAgICBsb2cgPSBsaXN0KCcvc2hhaGxhYi9hbHpoYW5nL2NsdXN0dG1wL3BhcGVyYW5hbHlzaXMyL2ltbXVuZV92YXJpYWJpbGl0eS5sb2cnKSwKICAgIHJlc291cmNlcyA9IGxpc3QoKSwKICAgIGNvbmZpZyA9IGxpc3QoInZfZGljdGlvbmFyeSIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3N1YnByb2plY3RzL2ltbXR5cGVyL21ldGFkYXRhL2ltZ3QvSG9tb19zYXBpZW5zX1RSQlYuZmFzdGEnLCAiY2xvbmVfYnJhbmNoX2xlbmd0aF9maWxlIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvZGF0YS9pdGgvY29tcGxldGUvYnJhbmNoX2RhdGEudHN2JywgImRyaXZlcl9hbmFseXNpc19ub3RlYm9vayIgPSAnUm1kL2RyaXZlcl9hbmFseXNpcy5SbWQnLCAiaXRoX3N0YXRfdHlwZXMiID0gYygnZW50cm9weScsICdwb3N0cHJvY2Vzc2VkX2RpdmVyZ2VuY2UnLCAnY29tYmluZWRfaXRoX25vcm1hbGl6ZWQnLCAncHJvcG9ydGlvbl9zdWJjbG9uYWwnKSwgInRpbHNfZm9yX2NsdXN0ZXIiID0gYygnRV9DRDhfZGVuc2l0eScsICdFX0NENF9kZW5zaXR5JywgJ0VfQ0QyMF9kZW5zaXR5JywgJ0VfUGxhc21hX2RlbnNpdHknLCAnU19DRDhfZGVuc2l0eScsICdTX0NENF9kZW5zaXR5JywgJ1NfQ0QyMF9kZW5zaXR5JywgJ1NfUGxhc21hX2RlbnNpdHknKSwgImludGVybWVkaWF0ZV9kaXIiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9yZXN1bHRzL2ludGVybWVkaWF0ZXMvcnVuMicsICJpbW10eXBlcl9sZW5ndGhzIiA9ICcxMSAxMiAxMyAxNCAxNSAxNiAxNyAxOCcsICJtb2xzdWJ0eXBlX3RpbHR5cGVzIiA9IGMoJ0VfQ0Q4X2RlbnNpdHknLCAnRV9DRDRfZGVuc2l0eScsICdFX0NEMjBfZGVuc2l0eScsICdFX1BsYXNtYV9kZW5zaXR5JywgJ1NfQ0Q4X2RlbnNpdHknLCAnU19DRDRfZGVuc2l0eScsICdTX0NEMjBfZGVuc2l0eScsICdTX1BsYXNtYV9kZW5zaXR5JyksICJ0YWJsZV9kaXIiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9yZXN1bHRzL3RhYmxlcy9ydW4yJywgInRpbF9jbGFzc2lmaWVyX25vdGVib29rIiA9ICdSbWQvdGlsX2NsYXNzaWZpZXIuUm1kJywgIm1tY3RtX292X2NvbWJpbmVkX3NpZ3Bsb3QiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9yZXN1bHRzL21tY3RtX3Jlc3VsdHMvY29tYmluZWRfb3ZfbW1jdG0vcGxvdHMvb3Zfc252LXN2X3NpZ3NfbXVsdGlwYW5lbC5wZGYnLCAibW1jdG1fZmluYWxfcGF0aWVudF9kaXIiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9yZXN1bHRzL21tY3RtX3Jlc3VsdHMvaXRoX2J5LXBhdGllbnRfd2l0aC1vdicsICJpaGNfeGNyX3RpbHR5cGVzIiA9IGMoJ0VfQ0Q4X2RlbnNpdHknLCAnRV9DRDRfZGVuc2l0eScsICdFX0NEMjBfZGVuc2l0eScsICdFX1BsYXNtYV9kZW5zaXR5JywgJ1NfQ0Q4X2RlbnNpdHknLCAnU19DRDRfZGVuc2l0eScsICdTX0NEMjBfZGVuc2l0eScsICdTX1BsYXNtYV9kZW5zaXR5JywgJ1RfQ0Q4X2RlbnNpdHknLCAnVF9DRDRfZGVuc2l0eScsICdUX0NEMjBfZGVuc2l0eScsICdUX1BsYXNtYV9kZW5zaXR5JyksICJpdGhfdGlsX25vdGVib29rIiA9ICdSbWQvaXRoX3RpbF9kZW5zaXRpZXMuUm1kJywgIml0aF9zdGF0aXN0aWNzX25vdGVib29rIiA9ICdSbWQvaXRoX3N0YXRpc3RpY3MuUm1kJywgInNwYXRpYWxfcmVzdWx0X2RpcnMiID0gbGlzdCgiZXBpdGhlbGlhbCIgPSAnL3NoYWhsYWIvYWx6aGFuZy9waXBlbGluZV9vdXRwdXRzL2l0aF9pbW11bmUvc3BhdHNpbS9pdGgzL2FiYycsICJzdHJvbWFsIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3BpcGVsaW5lX291dHB1dHMvaXRoX2ltbXVuZS9zcGF0c2ltL2l0aDUvYWJjJyksICJtdmNsdXN0X3RpbHR5cGVzIiA9IGMoJ0VfQ0Q4X2RlbnNpdHknLCAnRV9DRDRfZGVuc2l0eScsICdFX0NEMjBfZGVuc2l0eScsICdFX1BsYXNtYV9kZW5zaXR5JywgJ1NfQ0Q4X2RlbnNpdHknLCAnU19DRDRfZGVuc2l0eScsICdTX0NEMjBfZGVuc2l0eScsICdTX1BsYXNtYV9kZW5zaXR5JyksICJtdmNsdXN0X25jbHVzdCIgPSAzLCAic21vb3RoX3R5cGUiID0gJ2xvZXNzJywgImNsb25hbF9maWd1cmVfdGVtcGxhdGUiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9hbmFseXNpcy90ZW1wbGF0ZXMvY2xvbmFsX2ZpZ3VyZS5zdmcnLCAiY2xhc3NpZmllcl90eXBlIiA9ICdrbm4nLCAiaW1tdHlwZXJfbW9kZWxzIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcmVzdWx0cy9pbW10eXBlcl9yZXN1bHRzL2tsYXJlbmJlZWsvYWFfdmovZ3JhZGJvb3N0JywgImlncGFydGl0aW9uX291dGRpciIgPSAnL3NoYWhsYWIvYWx6aGFuZy9waXBlbGluZV9vdXRwdXRzL2l0aF9pbW11bmUvaWdwYXJ0aXRpb24vcnVuMjInLCAibmNsdXN0cyIgPSAzLCAiY2xvbmVfcHJldmFsZW5jZV9maWxlIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvZGF0YS9pdGgvY29tcGxldGUvY2xvbmVfZGF0YS50c3YnLCAieGNybWFwc2NhcGVfZmlsZXMiID0gYygnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3BhcGVyL3Jlc3VsdHMvaW50ZXJtZWRpYXRlcy9ydW4yL3hjcm1hcHNjYXBlLzEuc3ZnJywgJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9yZXN1bHRzL2ludGVybWVkaWF0ZXMvcnVuMi94Y3JtYXBzY2FwZS8yLnN2ZycsICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy9pbnRlcm1lZGlhdGVzL3J1bjIveGNybWFwc2NhcGUvMy5zdmcnLCAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3BhcGVyL3Jlc3VsdHMvaW50ZXJtZWRpYXRlcy9ydW4yL3hjcm1hcHNjYXBlLzQuc3ZnJywgJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9yZXN1bHRzL2ludGVybWVkaWF0ZXMvcnVuMi94Y3JtYXBzY2FwZS83LnN2ZycsICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy9pbnRlcm1lZGlhdGVzL3J1bjIveGNybWFwc2NhcGUvOS5zdmcnLCAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3BhcGVyL3Jlc3VsdHMvaW50ZXJtZWRpYXRlcy9ydW4yL3hjcm1hcHNjYXBlLzEwLnN2ZycsICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy9pbnRlcm1lZGlhdGVzL3J1bjIveGNybWFwc2NhcGUvMTEuc3ZnJywgJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9yZXN1bHRzL2ludGVybWVkaWF0ZXMvcnVuMi94Y3JtYXBzY2FwZS8xMi5zdmcnLCAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3BhcGVyL3Jlc3VsdHMvaW50ZXJtZWRpYXRlcy9ydW4yL3hjcm1hcHNjYXBlLzEzLnN2ZycsICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy9pbnRlcm1lZGlhdGVzL3J1bjIveGNybWFwc2NhcGUvMTQuc3ZnJywgJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9yZXN1bHRzL2ludGVybWVkaWF0ZXMvcnVuMi94Y3JtYXBzY2FwZS8xNS5zdmcnLCAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3BhcGVyL3Jlc3VsdHMvaW50ZXJtZWRpYXRlcy9ydW4yL3hjcm1hcHNjYXBlLzE2LnN2ZycsICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy9pbnRlcm1lZGlhdGVzL3J1bjIveGNybWFwc2NhcGUvMTcuc3ZnJyksICJuYW5vc3RyaW5nX2Fubm90YXRpb25zIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvZGF0YS9leHByZXNzaW9uL25hbm9zdHJpbmcvcGFuY2FuY2VyX2Fubm90YXRpb25zLnRzdicsICJpY2djX21vbGVjdWxhcl9zdWJ0eXBlcyIgPSAnL3NoYWhsYWIvYWx6aGFuZy9kYXRhL0lDR0MvaWNnY19wcmltYXJ5X3R1bW91cl9zdWJ0eXBlcy50c3YnLCAiYmNycGh5bG9fY29ycmVsYXRpb25zX25vdGVib29rIiA9ICdSbWQvYmNyX3BoeWxvX2NvcnJlbGF0aW9ucy5SbWQnLCAibW1jdG1fcGF0aWVudF9hbmNlc3RyYWxfZGVzY2VuZGFudF9yZXN1bHRfZGlyIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcmVzdWx0cy9tbWN0bV9yZXN1bHRzL2l0aF9ieS1wYXRpZW50LWFuY2VzdHJ5L291dHB1dCcsICJsaWJyYXJ5X3NpemVzIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3BpcGVsaW5lX291dHB1dHMvaXRoX2ltbXVuZS9taXhjci9taXhjcl9ydW5zL2l0aF8xXzJfMy9taXhjcjUvbGlicmFyeV9zaXplcy50c3YnLCAiaW5kZXhfbm90ZWJvb2siID0gJ1JtZC9pbmRleC5SbWQnLCAibXV0YXRpb25fc2lnbmF0dXJlX25vdGVib29rIiA9ICdSbWQvbXV0YXRpb25fc2lnbmF0dXJlcy5SbWQnLCAibW1jdG1fb3ZfY29tYmluZWRfcmVzdWx0X2RpciIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3Jlc3VsdHMvbW1jdG1fcmVzdWx0cy9jb21iaW5lZF9vdl9tbWN0bS9vdXRwdXQnLCAibG9nZGlyIiA9ICcvc2hhaGxhYi9hbHpoYW5nL2NsdXN0dG1wL3BhcGVyYW5hbHlzaXMyJywgInRpbHNfZm9yX3ZhcmlhYmlsaXR5IiA9IGMoJ0VfQ0Q4X2RlbnNpdHknLCAnRV9DRDRfZGVuc2l0eScsICdFX0NEMjBfZGVuc2l0eScsICdFX1BsYXNtYV9kZW5zaXR5JywgJ1NfQ0Q4X2RlbnNpdHknLCAnU19DRDRfZGVuc2l0eScsICdTX0NEMjBfZGVuc2l0eScsICdTX1BsYXNtYV9kZW5zaXR5JyksICJQTkdfUVVBTElUWSIgPSAzMDAsICJiY3JwaHlsb19leGFtcGxlc19ub3RlYm9vayIgPSAnUm1kL2Jjcl9waHlsb19leGFtcGxlcy5SbWQnLCAibW1jdG1fYW5jZXN0cmFsX2Rlc2NlbmRhbnRfcmVzdWx0X2RpciIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3Jlc3VsdHMvbW1jdG1fcmVzdWx0cy9pdGhfYnktYW5jZXN0cnktc2FtcGxlL291dHB1dCcsICJleGFtcGxlX21zYV9wbG90IiA9ICcvc2hhaGxhYi9hbHpoYW5nL3BpcGVsaW5lX291dHB1dHMvaXRoX2ltbXVuZS9pZ3BhcnRpdGlvbi9ydW4xMy9vbGQvYWxpZ25tZW50X3Bsb3RzL21zYS9pdGgyXzIvY2x1c3Q5L2luZGVsX3JldmVyc2VkLmh0bWwnLCAiaXRoX3Byb2plY3RfcmVzdWx0cyIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9pdGgzL2RhdGEvcmVzdWx0cycsICJtbWN0bV9maW5hbF9wYXRpZW50X3NpZ3Bsb3QiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9yZXN1bHRzL21tY3RtX3Jlc3VsdHMvaXRoX2J5LXBhdGllbnRfd2l0aC1vdi9wbG90cy9pdGgtYnktcGF0aWVudF9zbnYtc3Zfc2lnc19tdWx0aXBhbmVsLnBkZicsICJrbm93bl9zdWJ0eXBlc19tZXJnZWQiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9kYXRhL2V4cHJlc3Npb24va25vd25fc3VidHlwZXNfbWVyZ2VkLnRzdicsICJ4Y3JfbWFwcGluZ19ub3RlYm9vayIgPSAnUm1kL3hjcl9tYXBwaW5nLlJtZCcsICJtYXN0ZXJfdmFyaWFudF9maWxlIiA9ICcvc2hhaGxhYi9hbWNwaGVyc29uL3Byb2plY3RzL2l0aDMvaXRoMy9ub3RlYm9va3MvYmVzcG9rZS9pdGhfc252cy50c3YnLCAia25vd25fc3VidHlwZV9maWxlIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvZGF0YS9leHByZXNzaW9uL2FycmF5L3N1YnR5cGVzL2tub3duX3N1YnR5cGVzLnRzdicsICJ0Y2dhX2NsaW5pY2FsIiA9ICcvc2hhaGxhYi9hbHpoYW5nL2RhdGEvVENHQS9zeW5hcHNlX2NsaW5BbGxfZGF0YS50c3YnLCAibm90ZWJvb2tfZGlyIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy93ZWInLCAiZGIiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9tZXRhZGF0YS9kYi9pbW11bmVfcHJvamVjdC5zcWxpdGUzJywgInByb3BvcnRpb25fc3ViY2xvbmFsX2ZpbGUiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9kYXRhL2l0aC9jb21wbGV0ZS9vbGRfcHJvcG9ydGlvbl9zdWJjbG9uYWwudHN2JywgImloY19ydW4xIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvZGF0YS9paGMvY2Q4Y2QzY2QyMC92YWxpZGF0ZWRfc3RhdHNfd2VpZ2h0ZWRfbmV3LnJkYXRhJywgImJlbmNobWFya2RpciIgPSAnL3NoYWhsYWIvYWx6aGFuZy9iZW5jaG1hcmtzL3BhcGVyYW5hbHlzaXMyJywgIml0aF9zdGF0c19maWxlIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvZGF0YS9pdGgvY29tcGxldGUvY2xvbmFsX21lYXN1cmVzLnRzdicsICJiY3JfZGl2ZXJzaXR5IiA9ICcvc2hhaGxhYi9hbHpoYW5nL3BpcGVsaW5lX291dHB1dHMvaXRoX2ltbXVuZS9taXhjci9taXhjcl9ydW5zL2l0aF8xXzJfMy9taXhjcjUvcG9zdHByb2Nlc3MvSUdIL3Bvc3RmaWx0ZXJfZGl2ZXJzaXR5X3N0YXRzL2RpdmVyc2l0eS5zdHJpY3QucmVzYW1wbGVkLnR4dCcsICJmaWd1cmVfZ2FsbGVyeV9ub3RlYm9vayIgPSAnUm1kL2ZpZ3VyZXMuUm1kJywgIm1hcHNjYXBlX25vdGVib29rIiA9ICdSbWQvbWFwc2NhcGUuUm1kJywgImloY19ydW4yIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvZGF0YS9paGMvY2Q3OWNkMTM4Y2Q2OC92YWxpZGF0ZWRfc3RhdHNfd2VpZ2h0ZWQucmRhdGEnLCAic3BhdGlhbF9ub3RlYm9vayIgPSAnUm1kL3NwYXRpYWxfYW5hbHlzaXMuUm1kJywgIm1tY3RtX3NhbXBsZV9zaWdwbG90IiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcmVzdWx0cy9tbWN0bV9yZXN1bHRzL2l0aF9ieS1zYW1wbGUvcGxvdHMvaXRoLWJ5LXNhbXBsZV9zbnYtc3Zfc2lnc19tdWx0aXBhbmVsLnBkZicsICJpaGNfeGNyX3N0YXRzX25vdGVib29rIiA9ICdSbWQvaWhjX3hjcl9zdGF0cy5SbWQnLCAidGNyX2RpdmVyc2l0eSIgPSAnL3NoYWhsYWIvYWx6aGFuZy9waXBlbGluZV9vdXRwdXRzL2l0aF9pbW11bmUvbWl4Y3IvbWl4Y3JfcnVucy9pdGhfMV8yXzMvbWl4Y3I1L3Bvc3Rwcm9jZXNzL1RSQi9wb3N0ZmlsdGVyX2RpdmVyc2l0eV9zdGF0cy9kaXZlcnNpdHkuc3RyaWN0LnJlc2FtcGxlZC50eHQnLCAiZHJpdmVyX21hcCIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3N1YnByb2plY3RzL2RyaXZlcnMvZGF0YS9nZW5lX2xpc3RfbWFwcGVkLmJlZCcsICJtbWN0bV9zYW1wbGVfcmVzdWx0X2RpciIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3Jlc3VsdHMvbW1jdG1fcmVzdWx0cy9pdGhfYnktc2FtcGxlL291dHB1dCcsICJtdWx0aXZpZXdjbHVzdGVyaW5nX25vdGVib29rIiA9ICdSbWQvbXVsdGl2aWV3Y2x1c3RlcmluZy5SbWQnLCAiaWNnY19zcGVjaW1lbl9maWxlIiA9ICcvc2hhaGxhYi9hbHpoYW5nL2RhdGEvSUNHQy9zcGVjaW1lbi50c3YnLCAibW9sc3VidHlwZV9ub3RlYm9vayIgPSAnUm1kL21vbGVjdWxhcl9zdWJ0eXBlcy5SbWQnLCAiY2xvbmFsX3NhbXBsZXJzIiA9IGMoJ0hNQycsICdOVVRTJyksICJjbG9uZV90cmVlX2ZpbGUiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9kYXRhL2l0aC9jb21wbGV0ZS90cmVlX2RhdGEudHN2JywgIndhbmdfZmJpX3N0YXR1cyIgPSAnL3NoYWhsYWIvYWx6aGFuZy9kYXRhL0lDR0MvbmcuMzg0OS1TMTIudHh0JywgIm5lb2VkaXRpbmdfb3V0ZGlyIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3BpcGVsaW5lX291dHB1dHMvaXRoX2ltbXVuZS9uZW9lZGl0aW5nL3J1bjQnLCAidGNnYV9vdl9hbm5vdGF0aW9ucyIgPSAnL3NoYWhsYWIvYWx6aGFuZy9kYXRhL1RDR0EvdGNnYV9vdl9hbm5vdGF0aW9uX3N1cDEzLnR4dCcsICJ4Y3JfZGlzdGFuY2VfbWV0aG9kIiA9ICdob3JuJywgIm1hc3Rlcl9icmVha3BvaW50X2ZpbGUiID0gJy9zaGFobGFiL2FtY3BoZXJzb24vcHJvamVjdHMvaXRoMy9pdGgzL25vdGVib29rcy9iZXNwb2tlL2l0aF9icmVha3BvaW50cy50c3YnLCAibnNjYXR0ZXJzIiA9IDYsICJiY3JwaHlsb190aWx0eXBlcyIgPSBjKCdFX0NEOF9kZW5zaXR5JywgJ0VfQ0Q0X2RlbnNpdHknLCAnRV9DRDIwX2RlbnNpdHknLCAnRV9QbGFzbWFfZGVuc2l0eScsICdTX0NEOF9kZW5zaXR5JywgJ1NfQ0Q0X2RlbnNpdHknLCAnU19DRDIwX2RlbnNpdHknLCAnU19QbGFzbWFfZGVuc2l0eScsICdUX0NEOF9kZW5zaXR5JywgJ1RfQ0Q0X2RlbnNpdHknLCAnVF9DRDIwX2RlbnNpdHknLCAnVF9QbGFzbWFfZGVuc2l0eScpLCAicGF0aWVudHNfZm9yX2Nsb25hbCIgPSBjKDEsIDIsIDMsIDQsIDcsIDksIDEwLCAxMSwgMTIsIDEzLCAxNCwgMTUsIDE2LCAxNyksICJzYWRfbm90ZWJvb2siID0gJ1JtZC9zcGVjaWVzX2FidW5kYW5jZV9kaXN0cmlidXRpb25zLlJtZCcsICJpbW11bmVfdmFyaWFiaWxpdHlfbm90ZWJvb2siID0gJ1JtZC9pbW11bmVfdmFyaWFiaWxpdHkuUm1kJywgIm11dHNpZ190aWx0eXBlcyIgPSBjKCdFX0NEOF9kZW5zaXR5JywgJ0VfQ0Q0X2RlbnNpdHknLCAnRV9DRDIwX2RlbnNpdHknLCAnRV9QbGFzbWFfZGVuc2l0eScsICdTX0NEOF9kZW5zaXR5JywgJ1NfQ0Q0X2RlbnNpdHknLCAnU19DRDIwX2RlbnNpdHknLCAnU19QbGFzbWFfZGVuc2l0eScsICdUX0NEOF9kZW5zaXR5JywgJ1RfQ0Q0X2RlbnNpdHknLCAnVF9DRDIwX2RlbnNpdHknLCAnVF9QbGFzbWFfZGVuc2l0eScpLCAieGNybWFwc2NhcGVfdGNyX3BhdGllbnRfb3JkZXIiID0gYygxNSwgMSwgMywgNCwgMiwgMTcsIDcsIDE0LCA5LCAxMCwgMTIsIDEzLCAxMSwgMTYpLCAiaWNnY19jbGluaWNhbCIgPSAnL3NoYWhsYWIvYWx6aGFuZy9kYXRhL0lDR0MvZG9ub3IuT1YtQVUudHN2JywgInRjZ2FfZXhwcl9tYXRyaXgiID0gJy9zaGFobGFiL2FsemhhbmcvZGF0YS9UQ0dBL2V4cHJfbWF0cml4X25vcm1hbGl6ZV9zdGFuZGFyZGl6ZV9ub2R1cGxpY2F0ZXMudHN2JywgImRlZmF1bHRfc2FtcGxlciIgPSAnSE1DJywgInhjcl9jbG9uZXNfbm90ZWJvb2siID0gJ1JtZC94Y3JfY2xvbmVzX2FuYWx5c2lzLlJtZCcsICJ4Y3JfcWNfbm90ZWJvb2siID0gJ1JtZC9yZXBsaWNhdGVzLlJtZCcsICJqX2RpY3Rpb25hcnkiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9zdWJwcm9qZWN0cy9pbW10eXBlci9tZXRhZGF0YS9pbWd0L0hvbW9fc2FwaWVuc19UUkJKLmZhc3RhJywgIlBOR19ERU5TSVRZIiA9IDMwMCwgImJjcl9jbG9ub3R5cGVzIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3BpcGVsaW5lX291dHB1dHMvaXRoX2ltbXVuZS9taXhjci9taXhjcl9ydW5zL2l0aF8xXzJfMy9taXhjcjUvY2xvbm90eXBlcy9JR0hfY2xvbm90eXBlc19maWx0ZXJlZC50eHQnLCAieGNybWFwc2NhcGVfbm90ZWJvb2siID0gJ1JtZC94Y3JtYXBzY2FwZS5SbWQnLCAiaWNnY19leHByX21lbHRlZCIgPSAnL3NoYWhsYWIvYWx6aGFuZy9kYXRhL0lDR0MvT1ZBVV9leHByX21lbHRlZC50c3YnLCAibmFub3N0cmluZ19zaWduYXR1cmVfbm90ZWJvb2siID0gJ1JtZC9uYW5vc3RyaW5nX3NpZ25hdHVyZXMuUm1kJywgInByZXZhbGVuY2VfdGhyZXNob2xkIiA9IDAuMDEsICJleGFtcGxlX2Fubm90YXRpb25zIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3BpcGVsaW5lX291dHB1dHMvaXRoX2ltbXVuZS9pZ3BhcnRpdGlvbi9ydW4xMy9maW5hbF9wYXJ0aXRpb25zL2l0aDJfMi9jbHVzdDkvYW5ub3RhdGlvbnNfZmxhZ2dlZC50c3YnLCAic3VidHlwZV9tYXJrZXJfZmlsZSIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL2RhdGEvZXhwcmVzc2lvbi9uYW5vc3RyaW5nL3N1YnR5cGVfbWFya2Vycy50c3YnLCAiaWNnY19ub3JtYWxpemVkX3JlYWRzX21hdHJpeCIgPSAnL3NoYWhsYWIvYWx6aGFuZy9kYXRhL0lDR0MvT1ZBVV9leHByX21hdHJpeC50c3YnLCAibmFub3N0cmluZ19kYXRhIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcmVzdWx0cy9uYW5vc3RyaW5nX3Jlc3VsdHMvaXRoX2Z1bGwvcWMvbGltbWFfcXVhbnRpbGUvbm9ybWFsaXplZF9leHByZXNzaW9uX3ZvYV9sYWJlbHNfZmlsdGVyZWQudHN2JywgIm1tY3RtX3NhbXBsZV9hZF9zaWdwbG90IiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcmVzdWx0cy9tbWN0bV9yZXN1bHRzL2l0aF9ieS1hbmNlc3RyeS1zYW1wbGUvcGxvdHMvaXRoLWJ5LWFuY2VzdHJhbC1zYW1wbGVfc252LXN2X3NpZ3NfbXVsdGlwYW5lbC5wZGYnLCAibW1jdG1fcGF0aWVudF9hZF9zaWdwbG90IiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcmVzdWx0cy9tbWN0bV9yZXN1bHRzL2l0aF9ieS1wYXRpZW50LWFuY2VzdHJ5L3Bsb3RzL2l0aC1ieS1wYXRpZW50LWFuY2VzdHJ5X3Nudi1zdl9zaWdzX211bHRpcGFuZWwucGRmJywgInZhcmlhYmlsaXR5X3R5cGUiID0gJ3N0YWJpbGl6ZScsICJzaXRlX2ZpbGUiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9hbmFseXNpcy9SbWQvX3NpdGUueW1sJywgInBoZW5vdHlwZV90aHJlc2hvbGQiID0gMC44NSwgInRjcl9jbG9ub3R5cGVzIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3BpcGVsaW5lX291dHB1dHMvaXRoX2ltbXVuZS9taXhjci9taXhjcl9ydW5zL2l0aF8xXzJfMy9taXhjcjUvY2xvbm90eXBlcy9UUkJfY2xvbm90eXBlc19maWx0ZXJlZC50eHQnLCAibmVvYW50aWdlbl9lZGl0aW5nX25vdGVib29rIiA9ICdSbWQvaW1tdW5vZWRpdGluZy5SbWQnKSwKICAgIHJ1bGUgPSAnaW1tdW5lX3ZhcmlhYmlsaXR5JwopCiMjIyMjIyMjIE9yaWdpbmFsIHNjcmlwdCAjIyMjIyMjIyMKCiAgICAgICAgICAgICAgICAgICAgICAgIGBgYAoKCmBgYHtyIGdsb2JhbF9jaHVua19vcHRpb25zLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIHRpZHk9VFJVRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSkKYGBgCgoKYGBge3J9CmxpYnJhcnkoaXRoaS51dGlscykKbG9hZF9iYXNlX2xpYnMoKQpsaWJyYXJ5KEdlbm9taWNSYW5nZXMpCgpsaWJyYXJ5KG5hbm90b29scykKbGlicmFyeShpdGhpLm1ldGEpCmxpYnJhcnkoaXRoaS5paGMpCmxpYnJhcnkoaXRoaS54Y3IpCmxpYnJhcnkoaXRoaS5leHByKQpgYGAKCiMjIENvbG91ciBwYWxldHRlcwoKYGBge3J9CnBhbF9wYXRpZW50IDwtIHNlbGVjdF9wYWxldHRlKCJwYXRpZW50IikKYGBgCgojIyBQYXJhbWV0ZXJzCgpgYGB7cn0KZGJfcGF0aCA8LSBzbmFrZW1ha2VAcGFyYW1zJGRiCmloY190YWJsZV9wYXRoIDwtIHNuYWtlbWFrZUBpbnB1dCRpaGNfdGFibGUKaWhjX3RhYmxlX3NsaWRlX3BhdGggPC0gc25ha2VtYWtlQGlucHV0JGloY190YWJsZV9zbGlkZQoKeGNyX3RhYmxlX3BhdGggPC0gc25ha2VtYWtlQGlucHV0JHhjcl90YWJsZQpwcmV2YWxlbmNlX29wdGlvbiA8LSBzbmFrZW1ha2VAcGFyYW1zJHByZXZhbGVuY2Vfb3B0aW9uCmRpc3RhbmNlX21ldGhvZCA8LSBzbmFrZW1ha2VAcGFyYW1zJHhjcl9kaXN0YW5jZV9tZXRob2QKZmlsdGVyX3Rpc3N1ZSA8LSBzbmFrZW1ha2VAcGFyYW1zJHhjcl9maWx0ZXJfdGlzc3VlID09ICJmaWx0ZXIiCnRjcl9kaXZlcnNpdHlfZmlsZSA8LSBzbmFrZW1ha2VAaW5wdXQkdGNyX2RpdmVyc2l0eQpiY3JfZGl2ZXJzaXR5X2ZpbGUgPC0gc25ha2VtYWtlQGlucHV0JGJjcl9kaXZlcnNpdHkKCm5hbm9zdHJpbmdfZGF0YV9wYXRoIDwtIHNuYWtlbWFrZUBpbnB1dCRuYW5vc3RyaW5nX2RhdGEKbmFub3N0cmluZ19hbm5vdGF0aW9uc19wYXRoIDwtIHNuYWtlbWFrZUBpbnB1dCRuYW5vc3RyaW5nX2Fubm90YXRpb25zCgpub3JtYWxpemVfb3B0aW9uIDwtIHNuYWtlbWFrZUBwYXJhbXMkbm9ybWFsaXplICVpbiUgYygidHJ1ZSIsICJUUlVFIiwgIlRydWUiKQp0aWxfdmFyaWFuY2Vfb3B0aW9uIDwtIHNuYWtlbWFrZUBwYXJhbXMkdGlsX3ZhcmlhbmNlX29wdGlvbgpgYGAKCiMjIE1ldGFkYXRhCgpgYGB7cn0KZGIgPC0gc3JjX3NxbGl0ZShkYl9wYXRoLCBjcmVhdGU9RkFMU0UpCnNhbXBsZXMgPC0gY29sbGVjdCh0YmwoZGIsICJzYW1wbGVzIikpCmBgYAoKIyMgSUhDIGFuYWx5c2lzCgpgYGB7cn0KaWhjX3RhYmxlIDwtIGZyZWFkKGloY190YWJsZV9wYXRoKQppaGNfdGFibGVfc2xpZGUgPC0gZnJlYWQoaWhjX3RhYmxlX3NsaWRlX3BhdGgpCmBgYAoKYGBge3J9CmloY19zYW1wbGVzIDwtIGloY190YWJsZSRjb25kZW5zZWRfaWQKCmlmIChhbnkodGFibGUoaWhjX3NhbXBsZXMpICE9IDEpKSB7CiAgc3RvcCgiRHVwbGljYXRlIGNvbmRlbnNlZF9pZHMgZnJvbSB0aGUgc2FtZSBhbmFseXNpcyB0eXBlIChJSEMpLiIpCn0KYGBgCgojIyMgVG90YWwgVElMIGRlbnNpdGllcwoKYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD0zLjZ9CnRpbHR5cGVzIDwtIGMoIlRfQ0Q4X2RlbnNpdHkiLCAiVF9DRDRfZGVuc2l0eSIsCiAgICAgICAgICAgICAgIlRfQ0QyMF9kZW5zaXR5IiwgIlRfUGxhc21hX2RlbnNpdHkiKQoKcGxvdF9paGNfYmFycGxvdChpaGNfdGFibGUsIHRpbHR5cGVzLCBsYWJzPWMoIkNEOCsgVCBjZWxsIiwgIkNENCsgVCBjZWxsIiwgIkNEMjArIEIgY2VsbCIsICJQbGFzbWEgY2VsbCIpLCBZX01BWD0xNTAwLCBkYl9wYXRoID0gZGJfcGF0aCkKYGBgCgojIyMgVElMIGFuYXRvbWljIGRpZmZlcmVuY2VzCgpgYGB7cn0KdGlsdHlwZXMgPC0gYygiVF9DRDhfZGVuc2l0eSIsICJUX0NENF9kZW5zaXR5IiwgIlRfQ0QyMF9kZW5zaXR5IiwgIlRfUGxhc21hX2RlbnNpdHkiKQppaGNfZGF0YSA8LSBzdWJzZXQoaWhjX3RhYmxlLCBzZWxlY3Q9YygiY29uZGVuc2VkX2lkIiwgInBhdGllbnRfaWQiLCB0aWx0eXBlcykpCmloY19kYXRhJHRpc3N1ZSA8LSBleHRyYWN0X3Rpc3N1ZShpaGNfZGF0YSRjb25kZW5zZWRfaWQsIHR5cGUgPSAiZ2VuZXJhbCIsIHNob3J0ZW49VFJVRSkKCmloY19kYXRhX21lbHRlZCA8LSBtZWx0KGloY19kYXRhLCBpZC52YXJzID0gYygiY29uZGVuc2VkX2lkIiwgInBhdGllbnRfaWQiLCAidGlzc3VlIiksCiAgICAgbWVhc3VyZS52YXJzID0gdGlsdHlwZXMsIHZhcmlhYmxlLm5hbWUgPSAiVHlwZSIsIHZhbHVlLm5hbWUgPSAiRGVuc2l0eSIpCgpSRU5BTUVfTUFQIDwtIGxpc3QoCiAgIlRfQ0Q4X2RlbnNpdHkiPSJDRDgrIiwKICAiVF9DRDRfZGVuc2l0eSI9IkNENCsiLAogICJUX0NEMjBfZGVuc2l0eSI9IkNEMjArIiwKICAiVF9QbGFzbWFfZGVuc2l0eSI9IlBsYXNtYSIKKQoKaWhjX2RhdGFfbWVsdGVkJFR5cGUgPC0gbWFwdmFsdWVzKGloY19kYXRhX21lbHRlZCRUeXBlLCBmcm9tID0gbmFtZXMoUkVOQU1FX01BUCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0bz11bm5hbWUodW5saXN0KFJFTkFNRV9NQVApKSkKCiMjIENhbGN1bGF0ZSBwLXZhbHVlcwoKIyBXZSBoYXZlIG11bHRpcGxlIGxldmVscyBhbmQgbXVsdGlwbGUgcmFuZG9tIGVmZmVjdCBsZXZlbHMuIFJlc2lkdWFscyBhcmUgfmFwcHJveCBub3JtYWwuIAojIExldCdzIHVzZSBhIEdMTS4gSSBkb24ndCBrbm93IG9mIGEgbm9ucGFyYW1ldHJpYyBhbHRlcm5hdGl2ZSBmb3IgPj0zIFR4IGxldmVscy4gCnB2YWxzIDwtIHNldE5hbWVzKGRkcGx5KGloY19kYXRhX21lbHRlZCwgLihUeXBlKSwgZnVuY3Rpb24oeCkgewogIGRmIDwtIHN1YnNldChhcy5kYXRhLmZyYW1lKHgpLCAhaXMubmEoRGVuc2l0eSkpCiAgbW9kZWwgPC0gbG1lKERlbnNpdHkgfiB0aXNzdWUsIHJhbmRvbT1+MXxwYXRpZW50X2lkLCBkYXRhPWRmLCBtZXRob2Q9IlJFTUwiKQogIHJlc3VsdCA8LSBhbm92YS5sbWUobW9kZWwsIHR5cGU9InNlcXVlbnRpYWwiLCBhZGp1c3RTaWdtYT1GQUxTRSkKICAKICB0aXNzdWVfcHZhbCA8LSByZXN1bHQkYHAtdmFsdWVgWzJdCiAgZXEgPC0gc3Vic3RpdHV0ZShpdGFsaWMoUCk9PXAsIGxpc3QocD1mb3JtYXQodGlzc3VlX3B2YWwsIGRpZ2l0cz0zKSkpCiAgcmV0dXJuKGFzLmNoYXJhY3Rlcihhcy5leHByZXNzaW9uKGVxKSkpCn0pLCBjKCJUeXBlIiwgInAudmFsdWUiKSkKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTEwfQpwIDwtIGdncGxvdChpaGNfZGF0YV9tZWx0ZWQsIGFlcyh4PVR5cGUsIHk9RGVuc2l0eSkpICsgZ2VvbV9ib3hwbG90KGFlcyhmaWxsPXRpc3N1ZSkpICsgdGhlbWVfYncoKSArCiAgdGhlbWVfUHVibGljYXRpb24oKSArIHNjYWxlX2ZpbGxfUHVibGljYXRpb24oKSAgKyBmYWNldF93cmFwKH4gVHlwZSwgc2NhbGVzPSJmcmVlIiwgbmNvbD0yKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemU9MCksCiAgICAgICAgbGVnZW5kLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9NixmYW1pbHk9IkhlbHZldGljYSIpLAogICAgICAgIGF4aXMudGV4dC55PWVsZW1lbnRfdGV4dChzaXplPTYsZmFtaWx5PSJIZWx2ZXRpY2EiKSwKICAgICAgICBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTgsZmFtaWx5PSJIZWx2ZXRpY2EiKSkgKyBnZW9tX3RleHQoZGF0YT1wdmFscywgYWVzKHg9SW5mLCB5PUluZiwgbGFiZWw9cC52YWx1ZSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGp1c3Q9MS4xLCB2anVzdD0xLjUsc2l6ZT0zLHBhcnNlPVRSVUUpICsKICBzY2FsZV95X2NvbnRpbnVvdXModHJhbnM9bG9nMTBfdHJhbnMoKSwgYnJlYWtzPWMoMSwgMTAsIDEwMCwgMTAwMCkpCnAKYGBgCgoKIyMjIFRJTCBjb3JyZWxhdGlvbnMKCmBgYHtyfQppaGNtYXQgPC0gc3Vic2V0KGloY190YWJsZSwgc2VsZWN0PWMoIkVfQ0Q4X2RlbnNpdHkiLCAiRV9DRDRfZGVuc2l0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRV9DRDIwX2RlbnNpdHkiLCAiRV9QbGFzbWFfZGVuc2l0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiU19DRDhfZGVuc2l0eSIsICJTX0NENF9kZW5zaXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTX0NEMjBfZGVuc2l0eSIsICJTX1BsYXNtYV9kZW5zaXR5IikpCmNvbG5hbWVzKGloY21hdCkgPC0gYygiRSBDRDgrIiwgIkUgQ0Q0KyIsICJFIENEMjArIiwgIkUgUGxhc21hIiwgIlMgQ0Q4KyIsICJTIENENCsiLCAiUyBDRDIwKyIsCiAgICAgICAgICAgICAgICAgICAgICAiUyBQbGFzbWEiKQoKY29ycmVzIDwtIGNvcnIudGVzdChpaGNtYXQsIGFkanVzdD0iZmRyIikKY29ycmVzLnIgPC0gY29ycmVzJHIKY29ycmVzLnAgPC0gY29ycmVzJHAKaGMgPC0gaGNsdXN0KGRpc3QoY29ycmVzLnIpLCBtZXRob2Q9IndhcmQuRCIpCgpkaWFnKGNvcnJlcy5yKSA8LSBOQQpkaWFnKGNvcnJlcy5wKSA8LSBOQQoKY29ycmVzLmxhYmVscyA8LSBmb3JtYXQoc2lnbmlmKGNvcnJlcy5wLCAyKSwgMikKCmNvbHMgPC0gY29sb3JSYW1wUGFsZXR0ZShjKCIjOTVjYmVlIiwgIiNmZmQ3M2UiLCIjY2U0NzJlIikpKDEwMCkKYGBgCgpgYGB7ciBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9MTB9CnBhcihmYW1pbHk9J2hlbHZldGljYScpCnBoZWF0bWFwKGNvcnJlcy5yW2hjJG9yZGVyLGhjJG9yZGVyXSwgZGlzcGxheV9udW1iZXJzID0gY29ycmVzLmxhYmVscywgCiAgICAgICAgIGNvbG9yID0gY29scywgbnVtYmVyX2NvbG9yID0gIndoaXRlIiwgCiAgICAgICAgIGNsdXN0ZXJfcm93cyA9IEZBTFNFLCBjbHVzdGVyX2NvbHM9RkFMU0UpCmBgYAoKCiMjIFRDUi9CQ1IgYW5hbHlzaXMKCmBgYHtyfQp4Y3JfdGFibGUgPC0gcmVhZF9jbG9ub3R5cGVzKHhjcl90YWJsZV9wYXRoLCBkdXBsaWNhdGVzPUZBTFNFLCBkYl9wYXRoLCB2ZXJib3NlID0gMSkKCnhjcl9zYW1wbGVzIDwtIHVuaXF1ZSh4Y3JfdGFibGUkY29uZGVuc2VkX2lkKQpgYGAKCmBgYGB7cn0KdGNyX3NlZ21lbnRfdHlwZSA8LSAiVFJCIgpiY3Jfc2VnbWVudF90eXBlIDwtICJJR0giCgppZF90eXBlIDwtICJjb25kZW5zZWRfaWQiCmBgYAoKTnVtYmVyIG9mIHVuaXF1ZSBWT0FzOiBgciBsZW5ndGgodW5pcXVlKHhjcl90YWJsZSR2b2EpKWAsIG51bWJlciBvZiB1bmlxdWUgY29uZGVuc2VkX2lkczogYHIgbGVuZ3RoKHVuaXF1ZSh4Y3JfdGFibGUkY29uZGVuc2VkX2lkKSlgLgoKIyMjIENsb25vdHlwZSByZXBlcnRvaXJlcwoKIyMjIyBDaXJjb3MgCgojIyMjIyBUQ1IKCmBgYHtyfQp0Y3JfY2xvbm90eXBlcyA8LSBzdWJzZXQoeGNyX3RhYmxlLCB0eXBlID09IHRjcl9zZWdtZW50X3R5cGUpCgp0Y3JfY3Jvc3NfdGFibGUgPC0gY3Jvc3NfdGFidWxhdGUodGNyX2Nsb25vdHlwZXMsIGlkX3R5cGUgPSBpZF90eXBlKQoKaWYgKHByZXZhbGVuY2Vfb3B0aW9uICE9ICJmcmVxIikgewogIHN1bW1hcnkgPC0gdGNyX2Nsb25vdHlwZXMgJT4lIGdyb3VwX2J5KGNvbmRlbnNlZF9pZCkgJT4lIHN1bW1hcmlzZShuY2xvbmVzPWxlbmd0aChjZHIzbnQpKQogIGRpc3RzIDwtIGNvbXB1dGVfaW1tdW5lX2Rpc3RhbmNlX21hdHJpeCh0Y3JfY3Jvc3NfdGFibGUsIG1ldGhvZD0ibnVtX292ZXJsYXBzIikKfSBlbHNlIHsKICBzdW1tYXJ5IDwtIHRjcl9jbG9ub3R5cGVzICU+JSBncm91cF9ieShjb25kZW5zZWRfaWQpICU+JSBzdW1tYXJpc2UobnJlYWRzPXN1bShjb3VudCkpCiAgZGlzdHMgPC0gY29tcHV0ZV9pbW11bmVfZGlzdGFuY2VfbWF0cml4KHRjcl9jcm9zc190YWJsZSwgbWV0aG9kPSJudW1fcmVhZHMiKQp9CgpvdmVybGFwbWF0IDwtIHNldE5hbWVzKHJlc2hhcGUyOjptZWx0KGRpc3RzKSwgYygic2FtcGxlMSIsICJzYW1wbGUyIiwgImRpdnNoYXJlZCIpKQpvdmVybGFwbWF0IDwtIG1lcmdlKG92ZXJsYXBtYXQsIHNldE5hbWVzKHN1bW1hcnksIGMoInNhbXBsZTEiLCAiZGl2MSIpKSkKb3ZlcmxhcG1hdCA8LSBtZXJnZShvdmVybGFwbWF0LCBzZXROYW1lcyhzdW1tYXJ5LCBjKCJzYW1wbGUyIiwgImRpdjIiKSkpCgpvdmVybGFwbWF0IDwtIHN1YnNldChvdmVybGFwbWF0LCBzYW1wbGUxICE9IHNhbXBsZTIpCm92ZXJsYXBtYXQkc2FtcGxlMSA8LSBhcy5jaGFyYWN0ZXIob3ZlcmxhcG1hdCRzYW1wbGUxKQpvdmVybGFwbWF0JHNhbXBsZTIgPC0gYXMuY2hhcmFjdGVyKG92ZXJsYXBtYXQkc2FtcGxlMikKCmRmIDwtIGRhdGEuZnJhbWUoc2FtcGxlMSA9IG92ZXJsYXBtYXQkc2FtcGxlMSwKICAgICAgICAgICAgICAgICBzYW1wbGUyID0gb3ZlcmxhcG1hdCRzYW1wbGUyLAogICAgICAgICAgICAgICAgIHNoYXJlZCA9IG92ZXJsYXBtYXQkZGl2c2hhcmVkKQpgYGAKCmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9MTB9CmNpcmNvc19wbG90KGRmLCAic2hhcmVkIiwgaWRfdHlwZSA9IGlkX3R5cGUsIGRiX3BhdGggPSBkYl9wYXRoKQpgYGAKCgojIyMjIyBCQ1IKCmBgYHtyfQpiY3JfY2xvbm90eXBlcyA8LSBzdWJzZXQoeGNyX3RhYmxlLCB0eXBlID09IGJjcl9zZWdtZW50X3R5cGUpCgpiY3JfY3Jvc3NfdGFibGUgPC0gY3Jvc3NfdGFidWxhdGUoYmNyX2Nsb25vdHlwZXMsIGlkX3R5cGUgPSBpZF90eXBlKQoKaWYgKHByZXZhbGVuY2Vfb3B0aW9uICE9ICJmcmVxIikgewogIHN1bW1hcnkgPC0gYmNyX2Nsb25vdHlwZXMgJT4lIGdyb3VwX2J5KGNvbmRlbnNlZF9pZCkgJT4lIHN1bW1hcmlzZShuY2xvbmVzPWxlbmd0aChjZHIzbnQpKQogIGRpc3RzIDwtIGNvbXB1dGVfaW1tdW5lX2Rpc3RhbmNlX21hdHJpeChiY3JfY3Jvc3NfdGFibGUsIG1ldGhvZD0ibnVtX292ZXJsYXBzIikKfSBlbHNlIHsKICBzdW1tYXJ5IDwtIGJjcl9jbG9ub3R5cGVzICU+JSBncm91cF9ieShjb25kZW5zZWRfaWQpICU+JSBzdW1tYXJpc2UobnJlYWRzPXN1bShjb3VudCkpCiAgZGlzdHMgPC0gY29tcHV0ZV9pbW11bmVfZGlzdGFuY2VfbWF0cml4KGJjcl9jcm9zc190YWJsZSwgbWV0aG9kPSJudW1fcmVhZHMiKQp9CgpvdmVybGFwbWF0IDwtIHNldE5hbWVzKHJlc2hhcGUyOjptZWx0KGRpc3RzKSwgYygic2FtcGxlMSIsICJzYW1wbGUyIiwgImRpdnNoYXJlZCIpKQpvdmVybGFwbWF0IDwtIG1lcmdlKG92ZXJsYXBtYXQsIHNldE5hbWVzKHN1bW1hcnksIGMoInNhbXBsZTEiLCAiZGl2MSIpKSkKb3ZlcmxhcG1hdCA8LSBtZXJnZShvdmVybGFwbWF0LCBzZXROYW1lcyhzdW1tYXJ5LCBjKCJzYW1wbGUyIiwgImRpdjIiKSkpCgpvdmVybGFwbWF0IDwtIHN1YnNldChvdmVybGFwbWF0LCBzYW1wbGUxICE9IHNhbXBsZTIpCm92ZXJsYXBtYXQkc2FtcGxlMSA8LSBhcy5jaGFyYWN0ZXIob3ZlcmxhcG1hdCRzYW1wbGUxKQpvdmVybGFwbWF0JHNhbXBsZTIgPC0gYXMuY2hhcmFjdGVyKG92ZXJsYXBtYXQkc2FtcGxlMikKCmRmIDwtIGRhdGEuZnJhbWUoc2FtcGxlMSA9IG92ZXJsYXBtYXQkc2FtcGxlMSwKICAgICAgICAgICAgICAgICBzYW1wbGUyID0gb3ZlcmxhcG1hdCRzYW1wbGUyLAogICAgICAgICAgICAgICAgIHNoYXJlZCA9IG92ZXJsYXBtYXQkZGl2c2hhcmVkKQpgYGAKCmBgYHtyIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD0xMH0KY2lyY29zX3Bsb3QoZGYsICJzaGFyZWQiLCBpZF90eXBlID0gaWRfdHlwZSwgZGJfcGF0aCA9IGRiX3BhdGgpCmBgYAoKIyMjIENsb25vdHlwZSByZXBlcnRvaXJlIHNpbWlsYXJpdHkKCmBgYHtyfQpjcm9zc190YWJsZXMgPC0gbGlzdCh0Y3I9dGNyX2Nyb3NzX3RhYmxlLCBiY3I9YmNyX2Nyb3NzX3RhYmxlKQpgYGAKClRoZSBkaXN0YW5jZSBtZXRyaWMgYmVpbmcgdXNlZCBpcyBgciBkaXN0YW5jZV9tZXRob2RgLiAKCmBgYHtyfQpkaXN0YW5jZV9tYXRyaWNlcyA8LSBsYXBwbHkoY3Jvc3NfdGFibGVzLCBmdW5jdGlvbihjcm9zc190YWJsZSkgewogIGRpc3RtYXQgPC0gY29tcHV0ZV9pbW11bmVfZGlzdGFuY2VfbWF0cml4KGNyb3NzX3RhYmxlLCBtZXRob2QgPSBkaXN0YW5jZV9tZXRob2QpCiAgbWF0IDwtIGFzLm1hdHJpeChkaXN0bWF0KQogIHJldHVybihtYXQpCn0pCmBgYAoKCmBgYHtyfQpzaW1zX3JhdyA8LSBsYXBwbHkoZGlzdGFuY2VfbWF0cmljZXMsIGZ1bmN0aW9uKG1hdCkgewogIHNpbXMgPC0gYXZlcmFnZV9pbnRyYXBhdGllbnRfc2ltaWxhcml0eShtYXQsIGZpbHRlcl90aXNzdWUgPSBmaWx0ZXJfdGlzc3VlLCBpZF90eXBlID0gaWRfdHlwZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRiX3BhdGggPSBkYl9wYXRoLCByYXcgPSBUUlVFKQogIHNpbXMgPC0gc3Vic2V0KHNpbXMsIHNlbGVjdD0tYyhwYXRpZW50MikpCiAgY29sbmFtZXMoc2ltcykgPC0gbWFwdmFsdWVzKGNvbG5hbWVzKHNpbXMpLCBmcm9tID0gYygicGF0aWVudDEiKSwgdG8gPSBjKCJwYXRpZW50IikpCiAgcmV0dXJuKHNpbXMpCn0pCgpzaW1zX2F2ZyA8LSBsYXBwbHkoZGlzdGFuY2VfbWF0cmljZXMsIGZ1bmN0aW9uKG1hdCkgewogIGF2ZXJhZ2VfaW50cmFwYXRpZW50X3NpbWlsYXJpdHkobWF0LCBmaWx0ZXJfdGlzc3VlID0gZmlsdGVyX3Rpc3N1ZSwgaWRfdHlwZSA9IGlkX3R5cGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYl9wYXRoID0gZGJfcGF0aCwgcmF3ID0gRkFMU0UpCn0pCmBgYAoKYGBge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD02fQppZ25vcmUgPC0gbGFwcGx5KGxpc3QoInRjciIsICJiY3IiKSwgZnVuY3Rpb24oc2VnbWVudCkgewogIGRhdCA8LSBzaW1zX3Jhd1tbc2VnbWVudF1dCiAgcCA8LSBnZ3Bsb3QoZGF0LCBhZXMoeD1mYWN0b3IocGF0aWVudCksIHk9MS1kaXN0KSkgKyBnZW9tX2JveHBsb3QoYWVzKGZpbGw9ZmFjdG9yKHBhdGllbnQpKSkgKyB0aGVtZV9idygpICsgdGhlbWVfUHVibGljYXRpb24oKSArIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHBhbF9wYXRpZW50KSArIAogICAgeGxhYigiUGF0aWVudCIpICsgeWxhYigiUGFpcndpc2UgcmVwZXJ0b2lyZSBzaW1pbGFyaXR5IikgKyBndWlkZXMoZmlsbD1ndWlkZV9sZWdlbmQodGl0bGU9IlBhdGllbnQiKSkKICBwcmludChwKQp9KQpgYGAKCgojIyMjIENvcnJlbGF0aW9uIHdpdGggVElMIGRlbnNpdGllcwoKYGBge3J9Cm1lYW5fdGlsX2RlbnNpdGllcyA8LSBzdWJzZXQoaWhjX3RhYmxlLCBzZWxlY3Q9LWModm9hLCBzaXRlX2lkLCBjb25kZW5zZWRfaWQpKSAlPiUgCiAgZ3JvdXBfYnkocGF0aWVudF9pZCkgJT4lCiAgc3VtbWFyaXNlX2FsbChmdW5jdGlvbih4KSBtZWFuKHgsIG5hLnJtPVRSVUUpKQoKc2ltc19hdmdfdGNyX3RpbCA8LSBtZXJnZShzaW1zX2F2ZyR0Y3IsIG1lYW5fdGlsX2RlbnNpdGllcywgYnk9InBhdGllbnRfaWQiKQpzaW1zX2F2Z19iY3JfdGlsIDwtIG1lcmdlKHNpbXNfYXZnJGJjciwgbWVhbl90aWxfZGVuc2l0aWVzLCBieT0icGF0aWVudF9pZCIpCgpzaW1zX2F2Z190Y3JfdGlsJHBhdGllbnRfaWQgPC0gZmFjdG9yKHNpbXNfYXZnX3Rjcl90aWwkcGF0aWVudF9pZCkKc2ltc19hdmdfYmNyX3RpbCRwYXRpZW50X2lkIDwtIGZhY3RvcihzaW1zX2F2Z19iY3JfdGlsJHBhdGllbnRfaWQpCgpzaW1zX2F2Z190Y3JfdGlsIDwtIHN1YnNldChzaW1zX2F2Z190Y3JfdGlsLCBuY29tcGFyaXNvbnMgPj0gNikKc2ltc19hdmdfYmNyX3RpbCA8LSBzdWJzZXQoc2ltc19hdmdfYmNyX3RpbCwgbmNvbXBhcmlzb25zID49IDYpCmBgYAoKYGBge3J9CnRpbHR5cGVuYW1lcyA8LSBjb2xuYW1lcyhpaGNfdGFibGUpW3N0cl9kZXRlY3QoY29sbmFtZXMoaWhjX3RhYmxlKSwgIl4oVCkuKl9kZW5zaXR5JCIpXQoKc2ltc19hdmdfbWVsdGVkIDwtIGxhcHBseShsaXN0KHRjcj1zaW1zX2F2Z190Y3JfdGlsLCBiY3I9c2ltc19hdmdfYmNyX3RpbCksIGZ1bmN0aW9uKG1lcmdlZF9zaW1zKSB7CiAgb3RoZXJfY29scyA8LSBjb2xuYW1lcyhtZXJnZWRfc2ltcylbIWNvbG5hbWVzKG1lcmdlZF9zaW1zKSAlaW4lIHRpbHR5cGVuYW1lc10KICBtZWx0KG1lcmdlZF9zaW1zLCBpZC52YXJzID0gb3RoZXJfY29scywgbWVhc3VyZS52YXJzID0gdGlsdHlwZW5hbWVzLAogICAgICAgdmFyaWFibGUubmFtZSA9ICJUeXBlIiwgdmFsdWUubmFtZSA9ICJ2YWx1ZSIpCn0pCgpwdmFscyA8LSBsYXBwbHkoc2ltc19hdmdfbWVsdGVkLCBmdW5jdGlvbihtZXJnZWRfc2ltc19tZWx0KSB7CiAgc2V0TmFtZXMoZGRwbHkobWVyZ2VkX3NpbXNfbWVsdCwgLihUeXBlKSwgZnVuY3Rpb24oeCkgewogICAgZGYgPC0gc3Vic2V0KGFzLmRhdGEuZnJhbWUoeCksICFpcy5uYSh2YWx1ZSkpCiAgICBwdmFsIDwtIHdpdGgoZGYsIGNvci50ZXN0KG1lZGlhbl9zaW0sIHZhbHVlLCBtZXRob2QgPSAic3BlYXJtYW4iKSRwLnZhbHVlKQogICAgI2VxIDwtIHN1YnN0aXR1dGUoaXRhbGljKFApPT1wLCBsaXN0KHA9Zm9ybWF0KHB2YWwsIGRpZ2l0cz0zKSkpCiAgICAjcmV0dXJuKGFzLmNoYXJhY3Rlcihhcy5leHByZXNzaW9uKGVxKSkpCiAgICByZXR1cm4ocHZhbCkKICB9KSwgYygiVHlwZSIsICJwLnZhbHVlIikpCn0pCgpwdmFscyA8LSBsYXBwbHkocHZhbHMsIGZ1bmN0aW9uKHgpIHsKICB4JHAudmFsdWUgPC0gcC5hZGp1c3QoeCRwLnZhbHVlLCBtZXRob2QgPSAiZmRyIikKICB4JHAudmFsdWUgPC0gdW5saXN0KHNhcHBseSh4JHAudmFsdWUsIGZ1bmN0aW9uKHB2YWwpIGFzLmNoYXJhY3Rlcihhcy5leHByZXNzaW9uKHN1YnN0aXR1dGUoaXRhbGljKFApPT1wLCBsaXN0KHA9Zm9ybWF0KHB2YWwsIGRpZ2l0cz0zKSkpKSkpKQogIHJldHVybih4KQp9KQoKcHJpbnQocHZhbHMkdGNyKQoKcHJpbnQocHZhbHMkYmNyKQpgYGAKCmBgYHtyLCBmaWcud2lkdGg9NiwgZmlnLmhlaWdodD02fQpnZ3Bsb3Qoc2ltc19hdmdfdGNyX3RpbCwgYWVzKHg9bWVkaWFuX3NpbSwgeT1UX0NEOF9kZW5zaXR5KSkgKyBnZW9tX3BvaW50KGFlcyhjb2xvdXI9cGF0aWVudF9pZCkpICsgdGhlbWVfYncoKSArIHRoZW1lX1B1YmxpY2F0aW9uKCkgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gcGFsX3BhdGllbnQpICsgYW5ub3RhdGUoZ2VvbT0idGV4dCIsIGxhYmVsID0gc3Vic2V0KHB2YWxzJHRjciwgVHlwZSA9PSAiVF9DRDhfZGVuc2l0eSIpJHAudmFsdWUsIHg9SW5mLCB5PUluZiwgaGp1c3Q9MSwgdmp1c3Q9MSwgcGFyc2U9VFJVRSkgKyBzY2FsZV95X2xvZzEwKCkKCmdncGxvdChzaW1zX2F2Z190Y3JfdGlsLCBhZXMoeD1tZWRpYW5fc2ltLCB5PVRfQ0Q0X2RlbnNpdHkpKSArIGdlb21fcG9pbnQoYWVzKGNvbG91cj1wYXRpZW50X2lkKSkgKyB0aGVtZV9idygpICsgdGhlbWVfUHVibGljYXRpb24oKSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBwYWxfcGF0aWVudCkgKyBhbm5vdGF0ZShnZW9tPSJ0ZXh0IiwgbGFiZWwgPSBzdWJzZXQocHZhbHMkdGNyLCBUeXBlID09ICJUX0NENF9kZW5zaXR5IikkcC52YWx1ZSwgeD1JbmYsIHk9SW5mLCBoanVzdD0xLCB2anVzdD0xLCBwYXJzZT1UUlVFKSArIHNjYWxlX3lfbG9nMTAoKQoKZ2dwbG90KHNpbXNfYXZnX2Jjcl90aWwsIGFlcyh4PW1lZGlhbl9zaW0sIHk9VF9DRDIwX2RlbnNpdHkpKSArIGdlb21fcG9pbnQoYWVzKGNvbG91cj1wYXRpZW50X2lkKSkgKyB0aGVtZV9idygpICsgdGhlbWVfUHVibGljYXRpb24oKSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBwYWxfcGF0aWVudCkgKyBhbm5vdGF0ZShnZW9tPSJ0ZXh0IiwgbGFiZWwgPSBzdWJzZXQocHZhbHMkYmNyLCBUeXBlID09ICJUX0NEMjBfZGVuc2l0eSIpJHAudmFsdWUsIHg9SW5mLCB5PUluZiwgaGp1c3Q9MSwgdmp1c3Q9MSwgcGFyc2U9VFJVRSkgKyBzY2FsZV95X2xvZzEwKCkKCmdncGxvdChzaW1zX2F2Z19iY3JfdGlsLCBhZXMoeD1tZWRpYW5fc2ltLCB5PVRfUGxhc21hX2RlbnNpdHkpKSArIGdlb21fcG9pbnQoYWVzKGNvbG91cj1wYXRpZW50X2lkKSkgKyB0aGVtZV9idygpICsgdGhlbWVfUHVibGljYXRpb24oKSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBwYWxfcGF0aWVudCkgKyBhbm5vdGF0ZShnZW9tPSJ0ZXh0IiwgbGFiZWwgPSBzdWJzZXQocHZhbHMkYmNyLCBUeXBlID09ICJUX1BsYXNtYV9kZW5zaXR5IikkcC52YWx1ZSwgeD1JbmYsIHk9SW5mLCBoanVzdD0xLCB2anVzdD0xLCBwYXJzZT1UUlVFKSArIHNjYWxlX3lfbG9nMTAoKQpgYGAKClBlcmhhcHMgYSBiZXR0ZXIgd2F5IG9mIGRvaW5nIHRoaXMgaXMgdG8gdXNlIGdlbmVyYWxpemVkIGxpbmVhciBtb2RlbHM/CgpgYGB7cn0Kc3VtbWFyeShnbG0obWVkaWFuX3NpbSB+IFRfQ0Q4X2RlbnNpdHkgKyBUX0NENF9kZW5zaXR5ICsgVF9DRDIwX2RlbnNpdHkgKyBUX1BsYXNtYV9kZW5zaXR5LCBkYXRhPXNpbXNfYXZnX3Rjcl90aWwpKQpzdW1tYXJ5KGdsbShtZWRpYW5fc2ltIH4gVF9DRDhfZGVuc2l0eSArIFRfQ0Q0X2RlbnNpdHkgKyBUX0NEMjBfZGVuc2l0eSArIFRfUGxhc21hX2RlbnNpdHksIGRhdGE9c2ltc19hdmdfYmNyX3RpbCkpCmBgYAoKIyMjIFN1bW1hcnkgc3RhdHMKCiMjIyMgRGl2ZXJzaXR5CgpgYGB7cn0KcmljaG5lc3NfY29sdW1uIDwtICJvYnNlcnZlZERpdmVyc2l0eV9tZWFuIgpzaGFubm9uX2NvbHVtbiA8LSAic2hhbm5vbldpZW5lckluZGV4X21lYW4iCmludmVyc2Vfc2ltcHNvbl9jb2x1bW4gPC0gImludmVyc2VTaW1wc29uSW5kZXhfbWVhbiIKYGBgCgoKYGBge3J9CmRpdmVyc2l0eV9maWxlcyA8LSBsaXN0KHRjcj10Y3JfZGl2ZXJzaXR5X2ZpbGUsCiAgICAgICAgICAgICAgICAgICAgICAgIGJjcj1iY3JfZGl2ZXJzaXR5X2ZpbGUpCgpkaXZlcnNpdHkgPC0gbGFwcGx5KGRpdmVyc2l0eV9maWxlcywgZnVuY3Rpb24oZikgewogIHJlYWRfeGNyX2RpdmVyc2l0eV9maWxlKGYsIGRiX3BhdGgpCn0pCgpwbG90X2RpdmVyc2l0eSA8LSBmdW5jdGlvbihjb2wpIHsKICBpZ25vcmUgPC0gbGFwcGx5KGRpdmVyc2l0eSwgZnVuY3Rpb24oZGYpIHsKICAgIHAgPC0gZ2dwbG90KGRmLCBhZXNfc3RyaW5nKHg9InBhdGllbnRfaWQiLCB5PWNvbCkpICsgZ2VvbV9ib3hwbG90KGFlcyhmaWxsPXBhdGllbnRfaWQpKSArIHRoZW1lX2J3KCkgKyB0aGVtZV9QdWJsaWNhdGlvbigpICsgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFsX3BhdGllbnQpICsgeGxhYigiUGF0aWVudCIpICsgeWxhYigiRGl2ZXJzaXR5IikKICAgIHByaW50KHApCiAgfSkKfQpgYGAKCiMjIyMjIFJpY2huZXNzCgpgYGB7ciBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9Nn0KcGxvdF9kaXZlcnNpdHkocmljaG5lc3NfY29sdW1uKQpgYGAKCiMjIyMjIFNoYW5ub24gZW50cm9weQoKYGBge3IgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTZ9CnBsb3RfZGl2ZXJzaXR5KHNoYW5ub25fY29sdW1uKQpgYGAKCiMjIyMjIEludmVyc2UgU2ltcHNvbgoKYGBge3IgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTZ9CnBsb3RfZGl2ZXJzaXR5KGludmVyc2Vfc2ltcHNvbl9jb2x1bW4pCmBgYAoKIyMgTmFub3N0cmluZwoKYGBge3J9CmV4cHJzIDwtIGZyZWFkKG5hbm9zdHJpbmdfZGF0YV9wYXRoKQpsYWJlbHMgPC0gZnJlYWQobmFub3N0cmluZ19hbm5vdGF0aW9uc19wYXRoKQpgYGAKCmBgYHtyfQpleHBybWF0IDwtIGFzLmRhdGEuZnJhbWUoZ2V0RXhwcnMoZXhwcnMpKQphbm5vbWF0IDwtIGdldEFubm9zKGV4cHJzKQpyb3duYW1lcyhleHBybWF0KSA8LSBhbm5vbWF0JE5hbWUKCm5hbm9zdHJpbmdfc2FtcGxlcyA8LSBtYXBfaWQoY29sbmFtZXMoZXhwcm1hdCksIGZyb20gPSAidm9hIiwgdG89ImNvbmRlbnNlZF9pZCIsIGRiX3BhdGggPSBkYl9wYXRoKQpgYGAKCmBgYHtyfQppZiAoYW55KHRhYmxlKG5hbm9zdHJpbmdfc2FtcGxlcykgIT0gMSkpIHsKICBzdG9wKCJEdXBsaWNhdGUgY29uZGVuc2VkX2lkcyBmcm9tIHRoZSBzYW1lIGFuYWx5c2lzIHR5cGUgKE5hbm9zdHJpbmcpLiIpCn0KYGBgCgojIyMgQ2VsbC10eXBlIGV4cHJlc3Npb24gc2lnbmF0dXJlcwoKYGBge3J9CnBhdGllbnRfc3RyaW5ncyA8LSBhcy5jaGFyYWN0ZXIobWFwX2lkKGNvbG5hbWVzKGV4cHJtYXQpLCBmcm9tID0gInZvYSIsIHRvPSJwYXRpZW50X2lkIiwgZGJfcGF0aCA9IGRiX3BhdGgpKQoKIyMgVGhlcmUgYXJlIG5vIGR1cGxpY2F0ZSBjb25kZW5zZWRfaWRzIHdoZW4gbWFwcGluZyBmcm9tIEZGUEUgdm9hJ3MsIHNvIHdlIGNhbiBjb252ZXJ0IGhlcmUgZm9yIHJlYWRhYmlsaXR5CmNvbG5hbWVzKGV4cHJtYXQpIDwtIG5hbm9zdHJpbmdfc2FtcGxlcwoKYW5ub3RhdGlvbnMgPC0gbWVyZ2UoYW5ub21hdCwgbGFiZWxzLCBieS54PSJOYW1lIiwgYnkueT0iR2VuZSIpCnBhdGllbnRzIDwtIGRhdGEuZnJhbWUoUGF0aWVudD1wYXRpZW50X3N0cmluZ3MpCnJvd25hbWVzKHBhdGllbnRzKSA8LSBjb2xuYW1lcyhleHBybWF0KQoKY2VsbF90eXBlcyA8LSBzdWJzZXQoYW5ub3RhdGlvbnMsIGNlbGxfdHlwZSAhPSAiIikkY2VsbF90eXBlCmNlbGxfdHlwZV9nZW5lcyA8LSBzdWJzZXQoYW5ub3RhdGlvbnMsIGNlbGxfdHlwZSAhPSAiIikkTmFtZVtvcmRlcihjZWxsX3R5cGVzKV0KCmFubm9yb3cgPC0gYXMuZGF0YS5mcmFtZShzdWJzZXQoYW5ub3RhdGlvbnMsIHNlbGVjdD1jKGNlbGxfdHlwZSkpKQpyb3duYW1lcyhhbm5vcm93KSA8LSBhbm5vdGF0aW9ucyROYW1lCmNvbG5hbWVzKGFubm9yb3cpWzFdIDwtICJDZWxsIFR5cGUiCgphbm5vY29sIDwtIHBhdGllbnRzCgphbm5vY29sb3JzIDwtIGxpc3QoIkNlbGwgVHlwZSI9Z2V0X2NvbG91cl9wYWxldHRlKHVuaXF1ZShjZWxsX3R5cGVzKSksIAogICAgICAgICAgICAgICAgICAgUGF0aWVudD1zZWxlY3RfcGFsZXR0ZSgicGF0aWVudCIpKQoKbWF0IDwtIGNsaXBfdmFsdWVzKHQoc2NhbGUodChleHBybWF0W2NlbGxfdHlwZV9nZW5lcyxdKSkpLCBoaT0yLCBsbz0tMikKCmdhcHMgPC0gd2hpY2gocmV2KCFkdXBsaWNhdGVkKHJldihjZWxsX3R5cGVzW29yZGVyKGNlbGxfdHlwZXMpXSkpKSkKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTE2LCBmaWcuaGVpZ2h0PTE2fQpwaGVhdG1hcChtYXQsIGFubm90YXRpb25fcm93ID0gYW5ub3JvdywgYW5ub3RhdGlvbl9jb2wgPSBhbm5vY29sLCBhbm5vdGF0aW9uX2NvbG9ycyA9IGFubm9jb2xvcnMsIAogICAgICAgICBjbHVzdGVyX3Jvd3MgPSBGQUxTRSwgc2hvd19jb2xuYW1lcyA9IEZBTFNFLCBjbHVzdGVyaW5nX21ldGhvZCA9ICJ3YXJkLkQiLAogICAgICAgICBnYXBzX3JvdyA9IGdhcHMpCmBgYAoKIyMjIENlbGwtdHlwZSBtZXRhZ2VuZXMKCmBgYHtyfQpjZWxsdHlwZV9tYXRyaXggPC0gY3JlYXRlX2NlbGx0eXBlX21hdHJpeChleHBycywgbGFiZWxzLCBkYl9wYXRoKQpgYGAKCmBgYHtyLCBmaWcud2lkdGg9MTYsIGZpZy5oZWlnaHQ9NH0KY2VsbHR5cGVfbWF0cml4X3NjYWxlZCA8LSBjbGlwX3ZhbHVlcyh0KHNjYWxlKHQoY2VsbHR5cGVfbWF0cml4KSkpLCBoaT0yLCBsbz0tMikKCnBoZWF0bWFwKGNlbGx0eXBlX21hdHJpeF9zY2FsZWQsIGFubm90YXRpb25fY29sID0gYW5ub2NvbCwgYW5ub3RhdGlvbl9jb2xvcnMgPSBhbm5vY29sb3JzLCAKICAgICAgICAgY2x1c3Rlcl9yb3dzID0gVFJVRSwgc2hvd19jb2xuYW1lcyA9IEZBTFNFLCBjbHVzdGVyaW5nX21ldGhvZCA9ICJ3YXJkLkQiKQpgYGAKCiMjIyMgQ29ycmVsYXRpb24gd2l0aCBpbnRyYXBhdGllbnQgWENSIHZhcmlhYmlsaXR5CgpgYGB7cn0KY2VsbHR5cGVfZGYgPC0gZGF0YS5mcmFtZShjZWxsdHlwZV9tYXRyaXggJT4lIHQsIGNoZWNrLm5hbWVzID0gRkFMU0UpICU+JSByb3duYW1lc190b19jb2x1bW4odmFyID0gImNvbmRlbnNlZF9pZCIpCmNlbGx0eXBlX2RmJHBhdGllbnRfaWQgPC0gbWFwX2lkKGNlbGx0eXBlX2RmJGNvbmRlbnNlZF9pZCwgZnJvbSA9ICJjb25kZW5zZWRfaWQiLCB0bz0icGF0aWVudF9pZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRiX3BhdGgpCgptZWFuX2NlbGx0eXBlX2V4cHJlc3Npb24gPC0gc3Vic2V0KGNlbGx0eXBlX2RmLCBzZWxlY3Q9LWMoY29uZGVuc2VkX2lkKSkgJT4lIAogIGdyb3VwX2J5KHBhdGllbnRfaWQpICU+JQogIHN1bW1hcmlzZV9hbGwoZnVuY3Rpb24oeCkgbWVhbih4LCBuYS5ybT1UUlVFKSkKCnNpbXNfYXZnX3Rjcl9leHByIDwtIG1lcmdlKHNpbXNfYXZnJHRjciwgbWVhbl9jZWxsdHlwZV9leHByZXNzaW9uLCBieT0icGF0aWVudF9pZCIpCnNpbXNfYXZnX2Jjcl9leHByIDwtIG1lcmdlKHNpbXNfYXZnJGJjciwgbWVhbl9jZWxsdHlwZV9leHByZXNzaW9uLCBieT0icGF0aWVudF9pZCIpCgpzaW1zX2F2Z190Y3JfZXhwciRwYXRpZW50X2lkIDwtIGZhY3RvcihzaW1zX2F2Z190Y3JfZXhwciRwYXRpZW50X2lkKQpzaW1zX2F2Z19iY3JfZXhwciRwYXRpZW50X2lkIDwtIGZhY3RvcihzaW1zX2F2Z19iY3JfZXhwciRwYXRpZW50X2lkKQpgYGAKCmBgYHtyfQpleHBybmFtZXMgPC0gY29sbmFtZXMoY2VsbHR5cGVfZGYpWyFjb2xuYW1lcyhjZWxsdHlwZV9kZikgJWluJSBjKCJjb25kZW5zZWRfaWQiLCAicGF0aWVudF9pZCIpXQoKc2ltc19hdmdfbWVsdGVkIDwtIGxhcHBseShsaXN0KHRjcj1zaW1zX2F2Z190Y3JfZXhwciwgYmNyPXNpbXNfYXZnX2Jjcl9leHByKSwgZnVuY3Rpb24obWVyZ2VkX3NpbXMpIHsKICBvdGhlcl9jb2xzIDwtIGNvbG5hbWVzKG1lcmdlZF9zaW1zKVshY29sbmFtZXMobWVyZ2VkX3NpbXMpICVpbiUgZXhwcm5hbWVzXQogIG1lbHQobWVyZ2VkX3NpbXMsIGlkLnZhcnMgPSBvdGhlcl9jb2xzLCBtZWFzdXJlLnZhcnMgPSBleHBybmFtZXMsCiAgICAgICB2YXJpYWJsZS5uYW1lID0gIlR5cGUiLCB2YWx1ZS5uYW1lID0gInZhbHVlIikKfSkKCnB2YWxzIDwtIGxhcHBseShzaW1zX2F2Z19tZWx0ZWQsIGZ1bmN0aW9uKG1lcmdlZF9zaW1zX21lbHQpIHsKICBzZXROYW1lcyhkZHBseShtZXJnZWRfc2ltc19tZWx0LCAuKFR5cGUpLCBmdW5jdGlvbih4KSB7CiAgICBkZiA8LSBzdWJzZXQoYXMuZGF0YS5mcmFtZSh4KSwgIWlzLm5hKHZhbHVlKSkKICAgIHB2YWwgPC0gd2l0aChkZiwgY29yLnRlc3QobWVhbl9zaW0sIHZhbHVlLCBtZXRob2QgPSAic3BlYXJtYW4iKSRwLnZhbHVlKQogICAgZXEgPC0gc3Vic3RpdHV0ZShpdGFsaWMoUCk9PXAsIGxpc3QocD1mb3JtYXQocHZhbCwgZGlnaXRzPTMpKSkKICAgIHJldHVybihhcy5jaGFyYWN0ZXIoYXMuZXhwcmVzc2lvbihlcSkpKQogIH0pLCBjKCJUeXBlIiwgInAudmFsdWUiKSkKfSkKCnByaW50KHB2YWxzJHRjcikKCnByaW50KHB2YWxzJGJjcikKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTYsIGZpZy5oZWlnaHQ9Nn0KZ2dwbG90KHNpbXNfYXZnX3Rjcl9leHByLCBhZXMoeD1tZWFuX3NpbSwgeT1gQ0Q4IFQtY2VsbGApKSArIGdlb21fcG9pbnQoYWVzKGNvbG91cj1wYXRpZW50X2lkKSkgKyB0aGVtZV9idygpICsgdGhlbWVfUHVibGljYXRpb24oKSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBwYWxfcGF0aWVudCkgKyBhbm5vdGF0ZShnZW9tPSJ0ZXh0IiwgbGFiZWwgPSBzdWJzZXQocHZhbHMkdGNyLCBUeXBlID09ICJDRDggVC1jZWxsIikkcC52YWx1ZSwgeD1JbmYsIHk9SW5mLCBoanVzdD0xLCB2anVzdD0xLCBwYXJzZT1UUlVFKQoKZ2dwbG90KHNpbXNfYXZnX3Rjcl9leHByLCBhZXMoeD1tZWFuX3NpbSwgeT1gVCBoZWxwZXIgY2VsbGApKSArIGdlb21fcG9pbnQoYWVzKGNvbG91cj1wYXRpZW50X2lkKSkgKyB0aGVtZV9idygpICsgdGhlbWVfUHVibGljYXRpb24oKSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBwYWxfcGF0aWVudCkgKyBhbm5vdGF0ZShnZW9tPSJ0ZXh0IiwgbGFiZWwgPSBzdWJzZXQocHZhbHMkdGNyLCBUeXBlID09ICJUIGhlbHBlciBjZWxsIikkcC52YWx1ZSwgeD1JbmYsIHk9SW5mLCBoanVzdD0xLCB2anVzdD0xLCBwYXJzZT1UUlVFKQoKZ2dwbG90KHNpbXNfYXZnX3Rjcl9leHByLCBhZXMoeD1tZWFuX3NpbSwgeT1gVC1jZWxsYCkpICsgZ2VvbV9wb2ludChhZXMoY29sb3VyPXBhdGllbnRfaWQpKSArIHRoZW1lX2J3KCkgKyB0aGVtZV9QdWJsaWNhdGlvbigpICsgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IHBhbF9wYXRpZW50KSArIGFubm90YXRlKGdlb209InRleHQiLCBsYWJlbCA9IHN1YnNldChwdmFscyR0Y3IsIFR5cGUgPT0gIlQtY2VsbCIpJHAudmFsdWUsIHg9SW5mLCB5PUluZiwgaGp1c3Q9MSwgdmp1c3Q9MSwgcGFyc2U9VFJVRSkKCmdncGxvdChzaW1zX2F2Z190Y3JfZXhwciwgYWVzKHg9bWVhbl9zaW0sIHk9YFRoMSBjZWxsYCkpICsgZ2VvbV9wb2ludChhZXMoY29sb3VyPXBhdGllbnRfaWQpKSArIHRoZW1lX2J3KCkgKyB0aGVtZV9QdWJsaWNhdGlvbigpICsgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IHBhbF9wYXRpZW50KSArIGFubm90YXRlKGdlb209InRleHQiLCBsYWJlbCA9IHN1YnNldChwdmFscyR0Y3IsIFR5cGUgPT0gIlRoMSBjZWxsIikkcC52YWx1ZSwgeD1JbmYsIHk9SW5mLCBoanVzdD0xLCB2anVzdD0xLCBwYXJzZT1UUlVFKQoKZ2dwbG90KHNpbXNfYXZnX3Rjcl9leHByLCBhZXMoeD1tZWFuX3NpbSwgeT1gVGgyIGNlbGxgKSkgKyBnZW9tX3BvaW50KGFlcyhjb2xvdXI9cGF0aWVudF9pZCkpICsgdGhlbWVfYncoKSArIHRoZW1lX1B1YmxpY2F0aW9uKCkgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gcGFsX3BhdGllbnQpICsgYW5ub3RhdGUoZ2VvbT0idGV4dCIsIGxhYmVsID0gc3Vic2V0KHB2YWxzJHRjciwgVHlwZSA9PSAiVGgyIGNlbGwiKSRwLnZhbHVlLCB4PUluZiwgeT1JbmYsIGhqdXN0PTEsIHZqdXN0PTEsIHBhcnNlPVRSVUUpCgpnZ3Bsb3Qoc2ltc19hdmdfYmNyX2V4cHIsIGFlcyh4PW1lYW5fc2ltLCB5PWBCLWNlbGxgKSkgKyBnZW9tX3BvaW50KGFlcyhjb2xvdXI9cGF0aWVudF9pZCkpICsgdGhlbWVfYncoKSArIHRoZW1lX1B1YmxpY2F0aW9uKCkgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gcGFsX3BhdGllbnQpICsgYW5ub3RhdGUoZ2VvbT0idGV4dCIsIGxhYmVsID0gc3Vic2V0KHB2YWxzJGJjciwgVHlwZSA9PSAiQi1jZWxsIikkcC52YWx1ZSwgeD1JbmYsIHk9SW5mLCBoanVzdD0xLCB2anVzdD0xLCBwYXJzZT1UUlVFKQpgYGAKCiMjIEludHJhcGF0aWVudCB2YXJpYWJpbGl0eSAob3ZlcmFsbCkKClJlcXVpcmVzIHRoYXQgY29uZGVuc2VkX2lkcyBhcmUgbmV2ZXIgZHVwbGljYXRlZCBpbiBhbnkgYW5hbHlzaXMgdHlwZS4gSSd2ZSBpbnNlcnRlZCBjaGVja3MgaW50byBlYWNoIG9mIHRoZSBzZWN0aW9ucyBhYm92ZSB0byBlbnN1cmUgdGhpcy4gCgojIyMgTmFub3N0cmluZwoKV2UnbGwgd29yayB3aXRoIGNlbGwtdHlwZSBtZXRhZ2VuZXMgYXMgd29ya2luZyB3aXRoIGFsbCBleHByZXNzaW9uIHZhbHVlcyB3aWxsIGJlIGJpYXNlZCBieSB0aGUgbnVtYmVyIG9mIGdlbmVzIGFzc29jaWF0ZWQgd2l0aCBlYWNoIGNlbGwgdHlwZS4gCgojIyMgVElMIGRlbnNpdGllcwoKYGBge3J9CnRpbHR5cGVzIDwtIGMoIlRfQ0Q4X2RlbnNpdHkiLCAiVF9DRDRfZGVuc2l0eSIsCiAgICAgICAgICAgICAgIlRfQ0QyMF9kZW5zaXR5IiwgIlRfUGxhc21hX2RlbnNpdHkiKQoKaWhjX3NsaWRlX2RhdGEgPC0gc3Vic2V0KGloY190YWJsZV9zbGlkZSwgc2VsZWN0PWMoImNvbmRlbnNlZF9pZCIsICJwYXRpZW50X2lkIiwgInNsaWRlIiwgdGlsdHlwZXMpKQppaGNfbWVsdGVkIDwtIG1lbHQoaWhjX3NsaWRlX2RhdGEsIGlkLnZhcnMgPSBjKCJjb25kZW5zZWRfaWQiLCAicGF0aWVudF9pZCIsICJzbGlkZSIpLAogICAgICAgICAgICAgICAgICAgbWVhc3VyZS52YXJzID0gdGlsdHlwZXMsIHZhcmlhYmxlLm5hbWUgPSAidGlsdHlwZSIsCiAgICAgICAgICAgICAgICAgICB2YWx1ZS5uYW1lID0gImRlbnNpdHkiKQppaGNfbWVsdGVkJHRpbHR5cGUgPC0gYXMuY2hhcmFjdGVyKGloY19tZWx0ZWQkdGlsdHlwZSkKYGBgCgoKIyMjIFhDUgoKYGBge3J9CgpgYGAKCiMjIyBDb3JyZWxhdGlvbnMKCmBgYHtyfQpUX0NFTExfVFlQRVMgPC0gYygiVF9DRDhfZGVuc2l0eSIsICJUX0NENF9kZW5zaXR5IikKQl9DRUxMX1RZUEVTIDwtIGMoIlRfQ0QyMF9kZW5zaXR5IiwgIlRfUGxhc21hX2RlbnNpdHkiKQpOQU5PU1RSSU5HX1RfVFlQRVMgPC0gYygiQ0Q4IFQtY2VsbCIsICJDeXRvdG94aWMgY2VsbCIsICJULWNlbGwiLCAiVCBoZWxwZXIgY2VsbCIsICJUaDEgY2VsbCIsICJUaDIgY2VsbCIsICJUaDE3IGNlbGwiLCAiVHJlZyIsICJUY20iLCAiVGVtIikKTkFOT1NUUklOR19CX1RZUEVTIDwtIGMoIkItY2VsbCIsICJURkgiKQpYQ1JfVF9UWVBFUyA8LSBjKCJ0Y3IiKQpYQ1JfQl9UWVBFUyA8LSBjKCJiY3IiKQoKY29scyA8LSBjb2xvclJhbXBQYWxldHRlKGMoIiM5NWNiZWUiLCAiI2ZmZDczZSIsIiNjZTQ3MmUiKSkoMTAwKQoKY29ycGxvdCA8LSBmdW5jdGlvbih2YXIxLCB2YXIyLCBzdWIxLCBzdWIyKSB7CiAgY29yX2RmIDwtIFJlZHVjZShmdW5jdGlvbih4LHkpIG1lcmdlKHgseSwgYnk9InBhdGllbnRfaWQiKSwgbGlzdCh2YXIxLCB2YXIyKSkKICBjb3JfbWF0cml4IDwtIHN1YnNldChjb3JfZGYsIHNlbGVjdD0tYyhwYXRpZW50X2lkKSkKICAKICBjb3JyZXMgPC0gY29yci50ZXN0KGNvcl9tYXRyaXgsIG1ldGhvZCA9ICJwZWFyc29uIiwgYWRqdXN0ID0gIm5vbmUiKQogICNoYyA8LSBoY2x1c3QoZGlzdChjb3JyZXMkciksIG1ldGhvZD0id2FyZC5EIikKICBkaWFnKGNvcnJlcyRyKSA8LSBOQQogIGRpYWcoY29ycmVzJHApIDwtIE5BCiAgCiAgY29ycmVzLnIgPC0gY29ycmVzJHJbc3ViMSwgc3ViMl0KICBjb3JyZXMucCA8LSBjb3JyZXMkcFtzdWIxLCBzdWIyXQogIAogIGNvcnJlcy5wLmxhYmVscyA8LSBmb3JtYXQoc2lnbmlmKGNvcnJlcy5wLCAyKSwgMikKICAKICBwaGVhdG1hcChjb3JyZXMuciwgZGlzcGxheV9udW1iZXJzID0gY29ycmVzLnAubGFiZWxzLCAKICAgICAgICAgICBjb2xvciA9IGNvbHMsIG51bWJlcl9jb2xvciA9ICJ3aGl0ZSIsIAogICAgICAgICAgIGNsdXN0ZXJfcm93cyA9IFRSVUUsIGNsdXN0ZXJfY29scz1UUlVFKQp9CmBgYAoKCiMjIyMgVElMIGRlbnNpdGllcyB2cy4gWENSCgpgYGB7ciwgZmlnLndpZHRoPTYsIGZpZy5oZWlnaHQ9NH0KaW50ZXJzZWN0X3NhbXBsZXMgPC0gY29tcHV0ZV9zYW1wbGVfaW50ZXJzZWN0aW9uKHNhbXBsZXNfbGlzdCA9IGxpc3QoaWhjX3NhbXBsZXMsIHhjcl9zYW1wbGVzKSkKCnRpbF92YXIgPC0gY29tcHV0ZV9paGNfdmFyKGloY19tZWx0ZWQsIGloY190YWJsZSwgc2FtcGxlcyA9IGludGVyc2VjdF9zYW1wbGVzLCB0aWxfdmFyaWFuY2Vfb3B0aW9uID0gInN0YWJpbGl6ZSIsIHRpbHR5cGVzKQp4Y3JfdmFyIDwtIGNvbXB1dGVfeGNyX3ZhcihkaXN0YW5jZV9tYXRyaWNlcywgc2FtcGxlcyA9IGludGVyc2VjdF9zYW1wbGVzLCBmaWx0ZXJfdGlzc3VlID0gRkFMU0UsIGlkX3R5cGUgPSAiY29uZGVuc2VkX2lkIiwgZGJfcGF0aCkKCmloY19lbnRyaWVzIDwtIGMoVF9DRUxMX1RZUEVTLCBCX0NFTExfVFlQRVMpCnhjcl9lbnRyaWVzIDwtIGMoWENSX1RfVFlQRVMsIFhDUl9CX1RZUEVTKQoKY29ycGxvdCh0aWxfdmFyLCB4Y3JfdmFyLCBpaGNfZW50cmllcywgeGNyX2VudHJpZXMpCmBgYAoKIyMjIyBUSUwgZGVuc2l0aWVzIHZzLiBOYW5vc3RyaW5nCgpgYGB7ciwgZmlnLndpZHRoPTYsIGZpZy5oZWlnaHQ9NH0KaW50ZXJzZWN0X3NhbXBsZXMgPC0gY29tcHV0ZV9zYW1wbGVfaW50ZXJzZWN0aW9uKHNhbXBsZXNfbGlzdCA9IGxpc3QoaWhjX3NhbXBsZXMsIG5hbm9zdHJpbmdfc2FtcGxlcykpCgp0aWxfdmFyIDwtIGNvbXB1dGVfaWhjX3ZhcihpaGNfbWVsdGVkLCBpaGNfdGFibGUsIHNhbXBsZXMgPSBpbnRlcnNlY3Rfc2FtcGxlcywgdGlsX3ZhcmlhbmNlX29wdGlvbiA9ICJzdGFiaWxpemUiLCB0aWx0eXBlcykKZXhwcl92YXIgPC0gY29tcHV0ZV9leHByX3ZhcihjZWxsdHlwZV9tYXRyaXgsIHNhbXBsZXMgPSBpbnRlcnNlY3Rfc2FtcGxlcywgc2NhbGUgPSBUUlVFKQoKaWhjX2VudHJpZXMgPC0gYyhUX0NFTExfVFlQRVMsIEJfQ0VMTF9UWVBFUykKZXhwcl9lbnRyaWVzIDwtIGMoTkFOT1NUUklOR19UX1RZUEVTLCBOQU5PU1RSSU5HX0JfVFlQRVMpCgpjb3JwbG90KHRpbF92YXIsIGV4cHJfdmFyLCBpaGNfZW50cmllcywgZXhwcl9lbnRyaWVzKQpgYGAKCiMjIyBYQ1IgdnMuIE5hbm9zdHJpbmcKCmBgYHtyLCBmaWcud2lkdGg9NiwgZmlnLmhlaWdodD00fQppbnRlcnNlY3Rfc2FtcGxlcyA8LSBjb21wdXRlX3NhbXBsZV9pbnRlcnNlY3Rpb24oc2FtcGxlc19saXN0ID0gbGlzdCh4Y3Jfc2FtcGxlcywgbmFub3N0cmluZ19zYW1wbGVzKSkKCnhjcl92YXIgPC0gY29tcHV0ZV94Y3JfdmFyKGRpc3RhbmNlX21hdHJpY2VzLCBzYW1wbGVzID0gaW50ZXJzZWN0X3NhbXBsZXMsIGZpbHRlcl90aXNzdWUgPSBGQUxTRSwgaWRfdHlwZSA9ICJjb25kZW5zZWRfaWQiLCBkYl9wYXRoKQpleHByX3ZhciA8LSBjb21wdXRlX2V4cHJfdmFyKGNlbGx0eXBlX21hdHJpeCwgc2FtcGxlcyA9IGludGVyc2VjdF9zYW1wbGVzLCBzY2FsZSA9IFRSVUUpCgp4Y3JfZW50cmllcyA8LSBjKFhDUl9UX1RZUEVTLCBYQ1JfQl9UWVBFUykKZXhwcl9lbnRyaWVzIDwtIGMoTkFOT1NUUklOR19UX1RZUEVTLCBOQU5PU1RSSU5HX0JfVFlQRVMpCgpjb3JwbG90KHhjcl92YXIsIGV4cHJfdmFyLCB4Y3JfZW50cmllcywgZXhwcl9lbnRyaWVzKQpgYGAKCiMjIEludHJhcGF0aWVudCB2YXJpYWJpbGl0eSAocGFpcndpc2UpCgpXZSBjYW4gYWxzbyBsb29rIGF0IGNvbnNpc3RlbmN5IGluIGludHJhcGF0aWVudCBzaW1pbGFyaXR5IG9uIGEgcGFpcndpc2UgYmFzaXMuIAoKYGBge3J9CnNjYWxhcl9kaXN0X21ldGhvZCA8LSAibWFuaGF0dGFuIgoKaWhjX3N0YWJpbGl6ZWQgPC0gc3RhYmlsaXplX2loY192YXJpYW5jZXMoaWhjX3NsaWRlX2RhdGEsIGloY190YWJsZSwgdGlsdHlwZXMpCmloY19zdGFiaWxpemVkJGNvbmRlbnNlZF9pZCA8LSBmYWN0b3JfaWQoaWhjX3N0YWJpbGl6ZWQkY29uZGVuc2VkX2lkLCAiY29uZGVuc2VkX2lkIiwgZGJfcGF0aCkKaWhjX3NpbXMgPC0gcGFpcndpc2Vfc2ltaWxhcml0eV9mcmFtZShpaGNfc3RhYmlsaXplZCwgaWRfY29sID0gaWRfdHlwZSwgZ3JvdXAgPSAicGF0aWVudF9pZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kID0gc2NhbGFyX2Rpc3RfbWV0aG9kKQoKIyMgYWx3YXlzIGZpbHRlcl90aXNzdWUgPSBGQUxTRQp4Y3Jfc2ltc19yYXdfYWxsIDwtIGxhcHBseShuYW1lcyhkaXN0YW5jZV9tYXRyaWNlcyksIGZ1bmN0aW9uKHNlZ21lbnQpIHsKICBtYXQgPC0gZGlzdGFuY2VfbWF0cmljZXNbW3NlZ21lbnRdXQogIHNpbXMgPC0gYXZlcmFnZV9pbnRyYXBhdGllbnRfc2ltaWxhcml0eShtYXQsIGZpbHRlcl90aXNzdWUgPSBGQUxTRSwgaWRfdHlwZSA9IGlkX3R5cGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYl9wYXRoID0gZGJfcGF0aCwgcmF3ID0gVFJVRSkKICBzaW1zIDwtIHN1YnNldChzaW1zLCBzZWxlY3Q9LWMocGF0aWVudDIpKQogIGNvbG5hbWVzKHNpbXMpIDwtIG1hcHZhbHVlcyhjb2xuYW1lcyhzaW1zKSwgZnJvbSA9IGMoInBhdGllbnQxIiwgImRpc3QiKSwgdG8gPSBjKCJwYXRpZW50X2lkIiwgc2VnbWVudCkpCiAgcmV0dXJuKHNpbXMpCn0pCm5hbWVzKHhjcl9zaW1zX3Jhd19hbGwpIDwtIG5hbWVzKGRpc3RhbmNlX21hdHJpY2VzKQoKY2VsbHR5cGVfZGYgPC0gcm93bmFtZXNfdG9fY29sdW1uKGFzLmRhdGEuZnJhbWUodChjZWxsdHlwZV9tYXRyaXgpKSwgdmFyID0gaWRfdHlwZSkKY2VsbHR5cGVfZGYkcGF0aWVudF9pZCA8LSBtYXBfaWQoY2VsbHR5cGVfZGYkY29uZGVuc2VkX2lkLCBmcm9tID0gImNvbmRlbnNlZF9pZCIsIHRvPSJwYXRpZW50X2lkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGJfcGF0aCkKY2VsbHR5cGVfc2ltcyA8LSBwYWlyd2lzZV9zaW1pbGFyaXR5X2ZyYW1lKGNlbGx0eXBlX2RmLCBpZF9jb2wgPSBpZF90eXBlLCBncm91cCA9ICJwYXRpZW50X2lkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZD1zY2FsYXJfZGlzdF9tZXRob2QpCmBgYAoKIyMjIFRJTCBkZW5zaXRpZXMgdnMgWENSCgpgYGB7cn0KaWhjX3hjcl9zaW1zIDwtIFJlZHVjZShwbHlyOjpqb2luLCBjKHhjcl9zaW1zX3Jhd19hbGwsIGxpc3QoaWhjX3NpbXMpKSkKCmloY19lbnRyaWVzIDwtIGMoVF9DRUxMX1RZUEVTLCBCX0NFTExfVFlQRVMpCnhjcl9lbnRyaWVzIDwtIGMoWENSX1RfVFlQRVMsIFhDUl9CX1RZUEVTKQoKY29yX21hdHJpeCA8LSBzdWJzZXQoaWhjX3hjcl9zaW1zLCBzZWxlY3Q9YyhpaGNfZW50cmllcywgeGNyX2VudHJpZXMpKQoKY29ycmVzIDwtIGNvcnIudGVzdChjb3JfbWF0cml4LCBtZXRob2QgPSAic3BlYXJtYW4iLCBhZGp1c3QgPSAibm9uZSIpCiNoYyA8LSBoY2x1c3QoZGlzdChjb3JyZXMkciksIG1ldGhvZD0id2FyZC5EIikKZGlhZyhjb3JyZXMkcikgPC0gTkEKZGlhZyhjb3JyZXMkcCkgPC0gTkEKCmNvcnJlcy5yIDwtIGNvcnJlcyRyW2loY19lbnRyaWVzLCB4Y3JfZW50cmllc10KY29ycmVzLnAgPC0gY29ycmVzJHBbaWhjX2VudHJpZXMsIHhjcl9lbnRyaWVzXQoKY29ycmVzLnAubGFiZWxzIDwtIGZvcm1hdChzaWduaWYoY29ycmVzLnAsIDIpLCAyKQoKcGhlYXRtYXAoY29ycmVzLnIsIGRpc3BsYXlfbnVtYmVycyA9IGNvcnJlcy5wLmxhYmVscywgCiAgICAgICAgIGNvbG9yID0gY29scywgbnVtYmVyX2NvbG9yID0gIndoaXRlIiwgCiAgICAgICAgIGNsdXN0ZXJfcm93cyA9IFRSVUUsIGNsdXN0ZXJfY29scz1UUlVFKQpgYGAKCgpgYGB7cn0KeGNyX3NpbXNfbWVsdGVkIDwtIFJlZHVjZShwbHlyOjpqb2luLCB4Y3Jfc2ltc19yYXdfYWxsKQoKeGNyX3NpbXNfbWVsdGVkIDwtIG1lbHQoeGNyX3NpbXNfbWVsdGVkLCBpZC52YXJzID0gY29sbmFtZXMoeGNyX3NpbXNfbWVsdGVkKVshY29sbmFtZXMoeGNyX3NpbXNfbWVsdGVkKSAlaW4lIHhjcl9lbnRyaWVzXSwgbWVhc3VyZS52YXJzID0geGNyX2VudHJpZXMsIHZhcmlhYmxlLm5hbWUgPSAic2VnbWVudCIsIHZhbHVlLm5hbWUgPSAieGNyc2ltIikKCmloY19zaW1zX21lbHRlZCA8LSBtZWx0KGloY19zaW1zLCBpZC52YXJzID0gY29sbmFtZXMoaWhjX3NpbXMpWyFjb2xuYW1lcyhpaGNfc2ltcykgJWluJSBpaGNfZW50cmllc10sIG1lYXN1cmUudmFycyA9IGloY19lbnRyaWVzLCB2YXJpYWJsZS5uYW1lID0gInRpbHR5cGUiLCB2YWx1ZS5uYW1lID0gImloY3NpbSIpCgppaGNfeGNyX21lbHRlZCA8LSBtZXJnZShpaGNfc2ltc19tZWx0ZWQsIHhjcl9zaW1zX21lbHRlZCwgYnk9Yygic2FtcGxlMSIsICJzYW1wbGUyIiwgInBhdGllbnRfaWQiKSkKCmdncGxvdChpaGNfeGNyX21lbHRlZCwgYWVzKHg9aWhjc2ltLCB5PXhjcnNpbSkpICsgZ2VvbV9wb2ludChhZXMoY29sb3VyPWZhY3RvcihwYXRpZW50X2lkKSkpICsgCiAgZmFjZXRfd3JhcCh0aWx0eXBlIH4gc2VnbWVudCwgc2NhbGVzID0gImZyZWUiKSArIHRoZW1lX2J3KCkgKyB0aGVtZV9QdWJsaWNhdGlvbigpICsgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBwYWxfcGF0aWVudCkKCnN1bW1hcnkobG1lcih4Y3JzaW0gfiBpaGNzaW0gKyAoMXxwYXRpZW50X2lkKSwgZGF0YSA9IHN1YnNldChpaGNfeGNyX21lbHRlZCwgdGlsdHlwZSA9PSAiVF9DRDhfZGVuc2l0eSIgJiBzZWdtZW50ID09ICJ0Y3IiICYgIWlzLm5hKGloY3NpbSkgJiAhaXMubmEoeGNyc2ltKSkpKQpgYGAKCkNyaXRpY2FsbHksIFRDUiBzaW1pbGFyaXR5IGRvZXMgbm90IGNvcnJlbGF0ZSB3aXRoIENEOCsgb3IgQ0Q0KyBULWNlbGwgZGVuc2l0eSBzaW1pbGFyaXR5LiAKCiMjIyBUSUwgZGVuc2l0aWVzIHZzLiBOYW5vc3RyaW5nCgpgYGB7cn0KaWhjX2V4cHJfc2ltcyA8LSBSZWR1Y2UocGx5cjo6am9pbiwgbGlzdChpaGNfc2ltcywgY2VsbHR5cGVfc2ltcykpCgppaGNfZW50cmllcyA8LSBjKFRfQ0VMTF9UWVBFUywgQl9DRUxMX1RZUEVTKQpleHByX2VudHJpZXMgPC0gYyhOQU5PU1RSSU5HX1RfVFlQRVMsIE5BTk9TVFJJTkdfQl9UWVBFUykKCmNvcl9tYXRyaXggPC0gc3Vic2V0KGloY19leHByX3NpbXMsIHNlbGVjdD1jKGloY19lbnRyaWVzLCBleHByX2VudHJpZXMpKQoKY29ycmVzIDwtIGNvcnIudGVzdChjb3JfbWF0cml4LCBtZXRob2QgPSAic3BlYXJtYW4iLCBhZGp1c3QgPSAibm9uZSIpCiNoYyA8LSBoY2x1c3QoZGlzdChjb3JyZXMkciksIG1ldGhvZD0id2FyZC5EIikKZGlhZyhjb3JyZXMkcikgPC0gTkEKZGlhZyhjb3JyZXMkcCkgPC0gTkEKCmNvcnJlcy5yIDwtIGNvcnJlcyRyW2loY19lbnRyaWVzLCBleHByX2VudHJpZXNdCmNvcnJlcy5wIDwtIGNvcnJlcyRwW2loY19lbnRyaWVzLCBleHByX2VudHJpZXNdCgpjb3JyZXMucC5sYWJlbHMgPC0gZm9ybWF0KHNpZ25pZihjb3JyZXMucCwgMiksIDIpCgpwaGVhdG1hcChjb3JyZXMuciwgZGlzcGxheV9udW1iZXJzID0gY29ycmVzLnAubGFiZWxzLCAKICAgICAgICAgY29sb3IgPSBjb2xzLCBudW1iZXJfY29sb3IgPSAid2hpdGUiLCAKICAgICAgICAgY2x1c3Rlcl9yb3dzID0gVFJVRSwgY2x1c3Rlcl9jb2xzPVRSVUUpCmBgYAoKIyMjIFhDUiB2cy4gTmFub3N0cmluZwoKYGBge3J9Cnhjcl9leHByX3NpbXMgPC0gUmVkdWNlKHBseXI6OmpvaW4sIGMoeGNyX3NpbXNfcmF3X2FsbCwgbGlzdChjZWxsdHlwZV9zaW1zKSkpCgp4Y3JfZW50cmllcyA8LSBjKFhDUl9UX1RZUEVTLCBYQ1JfQl9UWVBFUykKZXhwcl9lbnRyaWVzIDwtIGMoTkFOT1NUUklOR19UX1RZUEVTLCBOQU5PU1RSSU5HX0JfVFlQRVMpCgpjb3JfbWF0cml4IDwtIHN1YnNldCh4Y3JfZXhwcl9zaW1zLCBzZWxlY3Q9Yyh4Y3JfZW50cmllcywgZXhwcl9lbnRyaWVzKSkKCmNvcnJlcyA8LSBjb3JyLnRlc3QoY29yX21hdHJpeCwgbWV0aG9kID0gInNwZWFybWFuIiwgYWRqdXN0ID0gIm5vbmUiKQojaGMgPC0gaGNsdXN0KGRpc3QoY29ycmVzJHIpLCBtZXRob2Q9IndhcmQuRCIpCmRpYWcoY29ycmVzJHIpIDwtIE5BCmRpYWcoY29ycmVzJHApIDwtIE5BCgpjb3JyZXMuciA8LSBjb3JyZXMkclt4Y3JfZW50cmllcywgZXhwcl9lbnRyaWVzXQpjb3JyZXMucCA8LSBjb3JyZXMkcFt4Y3JfZW50cmllcywgZXhwcl9lbnRyaWVzXQoKY29ycmVzLnAubGFiZWxzIDwtIGZvcm1hdChzaWduaWYoY29ycmVzLnAsIDIpLCAyKQoKcGhlYXRtYXAoY29ycmVzLnIsIGRpc3BsYXlfbnVtYmVycyA9IGNvcnJlcy5wLmxhYmVscywgCiAgICAgICAgIGNvbG9yID0gY29scywgbnVtYmVyX2NvbG9yID0gIndoaXRlIiwgCiAgICAgICAgIGNsdXN0ZXJfcm93cyA9IFRSVUUsIGNsdXN0ZXJfY29scz1UUlVFKQpgYGAKClRoZXNlIHJlc3VsdHMgYXJlbid0IHJlYWxseSBjb25zaXN0ZW50IHdpdGggdGhlIHBhdGllbnQtbGV2ZWwgcmVzdWx0cy4gSXQncyBpbXBvcnRhbnQgdG8gbm90ZSwgaG93ZXZlciwgdGhhdCB0aGVzZSBjb3JyZWxhdGlvbnMgYXJlIGNhbGN1bGF0ZWQgbmFpdmUgdG8gdGhlIHBhdGllbnQgbGFiZWxzLiBpLmUuIHRoZXJlJ3Mgbm8gY29udHJvbGxpbmcgZm9yIHBhdGllbnQgd2l0aCBhIHJhbmRvbSBpbnRlcmNlcHQgYW5kIHNsb3BlLCBldGMuIAoKVGhpbmdzIGJlY29tZSBtb3JlIGRpZmZpY3VsdCB0byBpbnRlcnByZXQgaWYgd2UgaW5jb3Jwb3JhdGUgcmFuZG9tIGludGVyY2VwdHMgYW5kIHNsb3Blcy4gVGhlcmUgY291bGQgYmUgaGlnaCBjb3JyZWxhdGlvbnMgd2l0aGluIHBhdGllbnRzIGJ1dCBsb3cgY29ycmVsYXRpb25zIGJldHdlZW4gcGF0aWVudHMsIGFuZCB2aWNlIHZlcnNhLiAKClRoaW5rIGFib3V0IGhvdyB3ZSBjYW4gc2hvdyB0aGlzIGluIGEgc3RhdGlzdGljYWxseSByb2J1c3Qgd2F5LiAKCiMjIElIQyBwYWlyd2lzZSBzaWduaWZpY2FuY2UgdGVzdGluZwoKYGBge3J9CnBhaXJ3aXNlX3Jlc3VsdHNfZmlsdGVyZWQgPC0gaWhjX3BhaXJ3aXNlX3NpZ25pZmljYW5jZShpaGNfdGFibGVfc2xpZGUsIHRpbHR5cGVzLCBkYl9wYXRoLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2xpZGVfY291bnRfZmlsdGVyID0gMTApCmBgYAoKYGBge3J9ClFfVkFMVUVfVEhSRVNIT0xEIDwtIDAuMDEKCnBhaXJ3aXNlX3Jlc3VsdHNfZmlsdGVyZWQkc2lnbmlmaWNhbnQgPC0gcGFpcndpc2VfcmVzdWx0c19maWx0ZXJlZCRxLnZhbHVlIDwgUV9WQUxVRV9USFJFU0hPTEQKCnBhaXJ3aXNlX3RpbHR5cGVfc3VtbWFyeSA8LSBwYWlyd2lzZV9yZXN1bHRzX2ZpbHRlcmVkICU+JSBncm91cF9ieShwYXRpZW50X2lkLCB0aWx0eXBlKSAlPiUKICBzdW1tYXJpc2UoYW55X3NpZ25pZmljYW50PWFueShzaWduaWZpY2FudCkpCgpwYWlyd2lzZV90aWx0eXBlX2NvdW50IDwtIHBhaXJ3aXNlX3RpbHR5cGVfc3VtbWFyeSAlPiUgZ3JvdXBfYnkodGlsdHlwZSkgJT4lIHN1bW1hcmlzZShucGF0aWVudHM9c3VtKGFueV9zaWduaWZpY2FudCwgbmEucm09VFJVRSkpCgpwYWlyd2lzZV90aWx0eXBlX2NvdW50CmBgYAoKCmBgYHtyfQp4IDwtIHBhaXJ3aXNlX3RpbHR5cGVfc3VtbWFyeSAlPiUgZ3JvdXBfYnkodGlsdHlwZSkgJT4lIGRvKHNpZ3ZlYz0uJGFueV9zaWduaWZpY2FudCkKeF9wYWlycyA8LSBhc190aWJibGUobWVyZ2UoeCx4LGJ5PWMoKSkpCnhfcGFpcnNfcCA8LSB4X3BhaXJzICU+JSBncm91cF9ieSh0aWx0eXBlLngsIHRpbHR5cGUueSkgJT4lIHN1bW1hcmlzZShwLnZhbHVlID0gcGNoaXNxKEdlbm9taWNSYW5nZXM6OnBoaWNvZWYodGFibGUodW5saXN0KHNpZ3ZlYy54KSwgdW5saXN0KHNpZ3ZlYy55KSkpXjIgKiBzdW0odGFibGUodW5saXN0KHNpZ3ZlYy54KSwgdW5saXN0KHNpZ3ZlYy55KSkpLCBkZiA9IDEsIGxvd2VyLnRhaWwgPSBGQUxTRSkpCnhfcGFpcnNfcCRxLnZhbHVlIDwtIHAuYWRqdXN0KHhfcGFpcnNfcCRwLnZhbHVlLCBtZXRob2QgPSAiZmRyIikKCnhfcGFpcnNfcApgYGAK