library(ithi.utils)
load_base_libs()
library(survival)
library(rms)
library(edgeR)
library(limma)
library(gage)
library(pathview)
library(DT)
library(ape)
library(phytools)
library(phylobase)

library(ithi.meta)
library(ithi.xcr)
library(ithi.ihc)
library(ithi.seq)
library(ithi.clones)
library(ithi.expr)
library(ithi.external)

Colour palettes

pal_patient <- select_palette("patient")

Parameters

db_path <- snakemake@params$db

mmctm_sample_result_dir <- snakemake@input$mmctm_sample_dir
mmctm_sample_ad_result_dir <- snakemake@input$mmctm_sample_ad_dir
mmctm_patient_ad_result_dir <- snakemake@input$mmctm_patient_ad_dir
mmctm_ov_combined_result_dir <- snakemake@input$mmctm_ov_combined_dir
mmctm_final_patient_dir <- snakemake@input$mmctm_final_patient_dir

mmctm_sample_sigplot <- snakemake@input$mmctm_sample_sigplot
mmctm_sample_ad_sigplot <- snakemake@input$mmctm_sample_ad_sigplot
mmctm_patient_ad_sigplot <- snakemake@input$mmctm_patient_ad_sigplot
mmctm_ov_combined_sigplot <- snakemake@input$mmctm_ov_combined_sigplot
mmctm_final_patient_sigplot <- snakemake@input$mmctm_final_patient_sigplot

master_variant_file <- snakemake@input$master_variant_file
master_breakpoint_file <- snakemake@input$master_breakpoint_file

ihc_table_path <- snakemake@input$ihc_table
ihc_table_slide_path <- snakemake@input$ihc_table_slide
tiltypes <- snakemake@params$mutsig_tiltypes

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

molecular_subtype_file <- snakemake@input$molecular_subtypes

icgc_expr_mat_file <- snakemake@input$icgc_expr_mat
icgc_specimen_file <- snakemake@input$icgc_specimen
icgc_subtype_file <- snakemake@input$icgc_subtypes
icgc_expr_raw_file <- snakemake@input$icgc_expr_melted
icgc_donor_file <- snakemake@input$icgc_clinical

tcga_expr_mat_file <- snakemake@input$tcga_expr_mat
tcga_ov_annotation_file <- snakemake@input$tcga_ov_annotation
tcga_donor_file <- snakemake@input$tcga_clinical

proportion_subclonality_file <- snakemake@input$subclonality

wang_icgc_fbi_status_file <- snakemake@input$wang_fbi_status

snv_cluster_files <- snakemake@input$snv_cluster_files
snv_cluster_patients <- snakemake@params$snv_cluster_patients

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

ith_icgc_batch_corrected_expression_file <- snakemake@input$ith_icgc_bc

Metadata

db <- src_sqlite(db_path, create = FALSE)
samples <- collect(tbl(db, "samples"))
read_mutsig_output <- function(dir, sample_table, external = FALSE) {
    prop_table_path <- Sys.glob(file.path(dir, "*_props.tsv"))
    sigs_path <- Sys.glob(file.path(dir, "../plots/*_multipanel.pdf"))
    
    prop_table <- fread(prop_table_path)
    
    sample_cols <- colnames(prop_table)[colnames(prop_table) != "signature"]
    ith_cols <- str_detect(sample_cols, "patient")
    
    patients <- str_extract(sample_cols[ith_cols], "(?<=patient_?)[0-9]+")
    site_ids <- str_replace_all(sample_cols[ith_cols], "patient_?[0-9]+_", "")
    
    FROM <- c("rectum_site_1", "rectum_site_2", "rectum_site_3", "rectum_site_4", 
        "pelvis_site_1", "pelvis_site_2", "cul_de_sac_site_1")
    TO <- c("rectum_site_1", "rectum_site_1", "rectum_site_2", "rectum_site_2", 
        "pelvis_site_1", "pelvis_site_1", "cul-de-sac_site_1")
    
    site_ids_fixed <- mapvalues(site_ids, from = FROM, to = TO)
    df <- data.frame(patient_id = patients, site_id = site_ids_fixed, old_id = sample_cols[ith_cols])
    if (length(df[with(df, patient_id == "11" & site_id == "left_ovary_site_2"), 
        ]$site_id) > 0) {
        df[with(df, patient_id == "11" & site_id == "left_ovary_site_2"), ]$site_id <- "left_ovary_site_3"
    }
    
    sample_subset <- unique(subset(sample_table, select = c("condensed_id", 
        "patient_id", "site_id")))
    
    df <- plyr::join(df, sample_subset)
    if (nrow(df) > 0) {
        df$is_ancestral <- 0
        df$is_ancestral[df$site_id == "ancestral"] <- 1
    }
    
    # colnames(prop_table)[ith_cols] <- make.unique(df$condensed_id)
    
    prop_final <- melt(prop_table, id.vars = c("signature"), measure.vars = sample_cols, 
        variable.name = "old_id", value.name = "proportion")
    if (external) {
        prop_final_merged <- plyr::join(df, prop_final, by = c("old_id"), type = "full")
    } else {
        prop_final_merged <- plyr::join(df, prop_final, by = c("old_id"), type = "inner")
    }
    
    prop_final_merged <- prop_final_merged %>% mutate(new_id = ifelse(site_id %in% 
        c("ancestral", "residual"), yes = paste(patient_id, site_id, sep = "_"), 
        no = condensed_id))
    na_idx <- is.na(prop_final_merged$new_id)
    prop_final_merged$new_id[na_idx] <- as.character(prop_final_merged$old_id[na_idx])
    
    result <- list(prop = prop_final_merged, sigplot = sigs_path)
    return(result)
}

sum_total_variant_counts <- function(variant_sample) {
    variant_sample %>% group_by(condensed_id, patient_id) %>% summarise(snv_count = sum(snv_count), 
        bkpt_count = sum(bkpt_count))
}

compute_mutsig_counts <- function(df) {
    df <- df %>% mutate(is_sv = as.numeric(str_detect(signature, "^SV")), sigcount = ifelse(as.character(site_id) == 
        "ancestral", yes = ifelse(is_sv, yes = ancestral_bkpt * proportion, 
        no = ancestral_snv * proportion), no = ifelse(is_sv, yes = bkpt_count * 
        proportion, no = snv_count * proportion)))
    return(df)
}

create_mutsig_matrix <- function(df, col = "proportion") {
    mat <- acast(df, new_id ~ signature, fun.aggregate = mean, value.var = col)
    return(mat)
}
variant_res <- summarize_variant_counts(master_variant_file, master_breakpoint_file, 
    db_path)

Read 5.9% of 682516 rows
Read 682516 rows and 9 (of 9) columns from 0.026 GB file in 00:00:03
variant_sample <- variant_res$sample
variant_patient <- variant_res$patient

variant_sample_sum <- sum_total_variant_counts(variant_sample)

ihc_table <- fread(ihc_table_path)
ihc_table_subset <- subset(ihc_table, select = c("condensed_id", tiltypes))
ihc_table_slide <- fread(ihc_table_slide_path)

Sample level

Signatures

Results

props_sample <- read_mutsig_output(mmctm_sample_result_dir, samples)
props_sample_prop <- subset(props_sample$prop, patient_id != "11")

props_sample_mat <- create_mutsig_matrix(props_sample_prop, col = "proportion")

props_sample_mat_scaled <- clip_values(scale(props_sample_mat), 2, -2)
sample_heat <- pheatmap(props_sample_mat_scaled, fontsize_row = 5, clustering_distance_rows = "correlation", 
    clustering_method = "ward.D2")

props_sample_merged <- Reduce(function(x, y) plyr::join(x, y), list(props_sample_prop, 
    variant_sample_sum, variant_patient, ihc_table_subset))

props_sample_merged <- compute_mutsig_counts(props_sample_merged)
COL <- "E_CD8_density"
measure <- "sigcount"

pvals <- setNames(ddply(props_sample_merged, .(signature), function(x) {
    df <- as.data.frame(x)
    df <- df[!is.na(df[, COL]), ]
    corres <- summary(lmer(as.formula(paste0(measure, " ~ ", COL, " + (1|patient_id)")), 
        df))
    
    pval <- unname(corres$coefficients[, 5][2])
    eq <- substitute(italic(P) == p, list(p = format(pval, digits = 3)))
    return(as.character(as.expression(eq)))
}), c("signature", "p.value"))

ggplot(props_sample_merged, aes_string(x = COL, y = measure)) + geom_point(aes(colour = patient_id)) + 
    scale_colour_manual(values = pal_patient) + facet_wrap(~signature, scales = "free") + 
    geom_text(data = pvals, aes(x = Inf, y = Inf, label = p.value), hjust = 1.1, 
        vjust = 1.5, size = 3, parse = TRUE) + theme_bw()

# x <- Reduce(function(x,y) merge(x,y, by=c('condensed_id')),
# list(rownames_to_column(as.data.frame(t(prop_matrix)), var =
# 'condensed_id'), ihc_table_subset)) x$patient_id <- map_id(x$condensed_id,
# from = 'condensed_id', to='patient_id', db_path) x <- subset(x, patient_id
# != '11') cols <- colnames(x)[!colnames(x) %in% c('condensed_id',
# 'patient_id')] rmat <- matrix(nrow=length(cols),ncol=length(cols)) pmat <-
# matrix(nrow=length(cols),ncol=length(cols)) for (i in 1:length(cols)) {
# for (j in i:length(cols)) { if (i != j) { col1 <- cols[i] col2 <- cols[j]
# formula <- paste0('`', col2, '`', '~', '`', col1, '`', '+ (1|patient_id)',
# sep=' ') res <- summary(lmer(formula, x)) pval <- tryCatch({
# unname(res$coefficients[,5][2]) }, error = function(e) { NA }) r <-
# unname(res$coefficients[,1][2]) rmat[i,j] <- rmat[j,i] <- r pmat[i,j] <-
# pmat[j,i] <- pval } else { rmat[i,j] <- 1 pmat[i,j] <- NA } } }
# resmat <- log10(pmat)*-sign(rmat) resmat[resmat == Inf] <- NA
# rownames(resmat) <- colnames(resmat) <- cols pheatmap(resmat,
# display_numbers = signif(pmat, 3), cluster_rows = FALSE, cluster_cols =
# FALSE, fontsize = 5)
# xmat <- x %>% select(-one_of('condensed_id', 'patient_id')) cormat <-
# corr.test(xmat, method='spearman', adjust='fdr') pheatmap(cormat$r,
# display_numbers = signif(cormat$p, 3), fontsize = 5)

Ancestral-descendant level (samples)

Signatures

Results

props_ad <- read_mutsig_output(mmctm_sample_ad_result_dir, samples)
props_ad_prop <- subset(props_ad$prop, patient_id != "11")

props_ad_mat <- create_mutsig_matrix(props_ad_prop, col = "proportion")

props_ad_mat_scaled <- clip_values(scale(props_ad_mat), 2, -2)
patient_heat <- pheatmap(props_ad_mat_scaled, fontsize_row = 5, clustering_distance_rows = "correlation")

props_ad_merged <- Reduce(function(x, y) plyr::join(x, y), list(props_ad_prop, 
    subset(variant_sample, is_ancestral == 0), variant_patient, ihc_table_subset))

props_ad_merged <- compute_mutsig_counts(props_ad_merged)
COL <- "E_CD8_density"
measure <- "sigcount"

props_ad_merged_descendant <- subset(props_ad_merged, site_id != "ancestral")

pvals <- setNames(ddply(props_ad_merged_descendant, .(signature), function(x) {
    df <- as.data.frame(x)
    df <- df[!is.na(df[, COL]), ]
    corres <- summary(lmer(as.formula(paste0(measure, " ~ ", COL, " + (1|patient_id)")), 
        df))
    
    pval <- unname(corres$coefficients[, 5][2])
    eq <- substitute(italic(P) == p, list(p = format(pval, digits = 3)))
    return(as.character(as.expression(eq)))
}), c("signature", "p.value"))

ggplot(props_ad_merged_descendant, aes_string(x = COL, y = measure)) + geom_point(aes(colour = patient_id)) + 
    scale_colour_manual(values = pal_patient) + facet_wrap(~signature, scales = "free") + 
    geom_text(data = pvals, aes(x = Inf, y = Inf, label = p.value), hjust = 1.1, 
        vjust = 1.5, size = 3, parse = TRUE) + theme_bw()

What we can also see from these plots is that not resolving SNV-7 (the noise cluster) is going to be a problem. The signature proportion values from that cluster are fairly similar to those from SNV-6, the APOBEC signature.

Ancestral-descendant level (patients)

Signatures

Results

props_patient_ad <- read_mutsig_output(mmctm_patient_ad_result_dir, samples)
props_patient_ad_prop <- subset(props_patient_ad$prop, patient_id != "11")

props_patient_ad_mat <- create_mutsig_matrix(props_patient_ad_prop, col = "proportion")

props_patient_ad_mat_scaled <- clip_values(scale(props_patient_ad_mat), 2, -2)
patient_ad_heat <- pheatmap(props_patient_ad_mat_scaled, fontsize_row = 5, clustering_distance_rows = "correlation")

props_patient_ad_merged <- Reduce(function(x, y) plyr::join(x, y), list(props_patient_ad_prop, 
    subset(variant_sample, is_ancestral == 0), variant_patient))

props_patient_ad_merged <- compute_mutsig_counts(props_patient_ad_merged)

Correlation matrices

ihc_mat <- as.data.frame(ihc_table_subset %>% dplyr::select(-condensed_id))
rownames(ihc_mat) <- ihc_table_subset$condensed_id

# TOTAL_TIL_COLS <- c('T_CD8_density', 'T_CD4_density', 'T_CD20_density',
# 'T_Plasma_density') ihc_mat <- subset(ihc_mat, select=TOTAL_TIL_COLS)
compute_ihc_mutsig_cor <- function(ihc_mat, mutsig_mat, patient_summary = FALSE, 
    ancestral = FALSE) {
    if (patient_summary) {
        ihc_mat <- rownames_to_column(as.data.frame(ihc_mat), "id")
        mutsig_mat <- rownames_to_column(as.data.frame(mutsig_mat), "id")
        
        if (ancestral) {
            mutsig_mat <- subset(mutsig_mat, str_detect(id, "ancestral"))
        } else {
            mutsig_mat <- subset(mutsig_mat, !str_detect(id, "ancestral"))
        }
        
        ihc_mat$patient_id <- str_extract(ihc_mat$id, "^[0-9]+")
        mutsig_mat$patient_id <- str_extract(mutsig_mat$id, "^[0-9]+")
        
        ihc_mat <- ihc_mat %>% dplyr::select(-id) %>% group_by(patient_id) %>% 
            summarise_each(funs(mean(., na.rm = TRUE))) %>% column_to_rownames(var = "patient_id")
        mutsig_mat <- mutsig_mat %>% dplyr::select(-id) %>% group_by(patient_id) %>% 
            summarise_each(funs(mean(., na.rm = TRUE))) %>% column_to_rownames(var = "patient_id")
        
        ihc_mat <- as.matrix(ihc_mat)
        mutsig_mat <- as.matrix(mutsig_mat)
    }
    
    intersect_samples <- intersect(rownames(mutsig_mat), rownames(ihc_mat))
    mutsig <- mutsig_mat[intersect_samples, , drop = FALSE]
    ihc <- ihc_mat[intersect_samples, , drop = FALSE]
    
    pmat <- matrix(nrow = ncol(mutsig), ncol = ncol(ihc))
    rmat <- matrix(nrow = ncol(mutsig), ncol = ncol(ihc))
    rownames(pmat) <- rownames(rmat) <- colnames(mutsig)
    colnames(pmat) <- colnames(rmat) <- colnames(ihc)
    
    for (i in 1:ncol(mutsig)) {
        for (j in 1:ncol(ihc)) {
            corres <- cor.test(mutsig[, i], ihc[, j], method = "spearman")
            pmat[i, j] <- corres$p.value
            rmat[i, j] <- corres$estimate
        }
    }
    
    return(list(p = pmat, r = rmat))
}

For adjusted p-values just run p.adjust.mat on the p-value labels.

Sample-level

ihc_sample_cor <- compute_ihc_mutsig_cor(ihc_mat, props_sample_mat)

pheatmap(ihc_sample_cor$r, display_numbers = signif(ihc_sample_cor$p, 3))

ihc_sample_cor_summary <- compute_ihc_mutsig_cor(ihc_mat, props_sample_mat, 
    patient_summary = TRUE)

pheatmap(ihc_sample_cor_summary$r, display_numbers = signif(ihc_sample_cor_summary$p, 
    3))

Ancestral-descendant level

ihc_ad_cor <- compute_ihc_mutsig_cor(ihc_mat, props_ad_mat)

pheatmap(ihc_ad_cor$r, display_numbers = signif(ihc_ad_cor$p, 3))

ihc_ad_cor_summary <- compute_ihc_mutsig_cor(ihc_mat, props_ad_mat, patient_summary = TRUE)

pheatmap(ihc_ad_cor_summary$r, display_numbers = signif(ihc_ad_cor_summary$p, 
    3))

ihc_ad_cor_summary_ancestral <- compute_ihc_mutsig_cor(ihc_mat, props_ad_mat, 
    patient_summary = TRUE, ancestral = TRUE)

pheatmap(ihc_ad_cor_summary_ancestral$r, display_numbers = signif(ihc_ad_cor_summary_ancestral$p, 
    3))

Note: p-values shown are uncorrected. Patient-summarized correlations are insignificant after FDR correction.

Cluster-level analysis

Finding correlations at the level of individual signatures can be difficult. Even moreso because some published signatures are combinations of these signatures – e.g. SV-3 and SV-6 are both foldback signatures in the ancestral-descendant analysis.

Hence, we can look at the level of clusters from our heatmaps.

make_cluster_frame <- function(clusters) {
    clusts <- rownames_to_column(as.data.frame(clusters), "new_id")
    colnames(clusts)[2] <- "cluster"
    clusts$cluster <- factor(clusts$cluster)
    return(clusts)
}

NCLUST <- 2
selected_cols <- c("new_id", "condensed_id", "patient_id", "old_id", "cluster", 
    tiltypes)

Sample-level

clusters <- cutree(sample_heat$tree_row, NCLUST)
sample_clusts <- make_cluster_frame(clusters)

props_sample_merged_clusts <- join(props_sample_merged, sample_clusts)
sample_df <- unique(subset(props_sample_merged_clusts, select = selected_cols))

sample_df_melted <- melt(sample_df, id.vars = colnames(sample_df)[!colnames(sample_df) %in% 
    tiltypes], measure.vars = tiltypes, variable.name = "tiltype", value.name = "density")
pvals <- setNames(ddply(sample_df_melted, .(tiltype), function(x) {
    df <- as.data.frame(x)
    corres <- wilcox.test(density ~ cluster, df)
    
    pval <- corres$p.value
    eq <- substitute(italic(P) == p, list(p = format(pval, digits = 3)))
    return(as.character(as.expression(eq)))
}), c("tiltype", "p.value"))


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

diversity_column <- "observedDiversity_mean"

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

diversity <- lapply(names(diversity_files), function(segment) {
    f <- diversity_files[[segment]]
    xcr_diversity <- read_xcr_diversity_file(f, db_path)
    
    df <- subset(xcr_diversity, select = c("condensed_id", "patient_id", diversity_column))
    colnames(df) <- mapvalues(colnames(df), from = diversity_column, to = segment)
    return(df)
})
names(diversity) <- names(diversity_files)

xcr_diversity <- Reduce(plyr::join, diversity)

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

proportion_subclonality <- fread(proportion_subclonality_file)
proportion_subclonality_subset <- subset(proportion_subclonality, select = c("condensed_id", 
    "proportion_subclonal"))

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

celltype_matrix <- create_celltype_matrix(exprs, labels, db_path)
pathway_matrix <- create_pathway_matrix(exprs, labels, db_path)

celltype_df <- rownames_to_column(as.data.frame(t(celltype_matrix)), var = "condensed_id")
pathway_df <- rownames_to_column(as.data.frame(t(pathway_matrix)), var = "condensed_id")

NANOSTRING_VARS <- c(rownames(celltype_matrix), rownames(pathway_matrix))
colnames(sample_clusts) <- mapvalues(colnames(sample_clusts), "new_id", "condensed_id")

ihc_stabilize_subset <- stabilize_ihc_variances(ihc_table_slide, ihc_table, 
    tiltypes)

data_matrices <- list(sample_clusts, ihc_stabilize_subset, xcr_diversity, celltype_df, 
    pathway_df, proportion_subclonality_subset)
data_matrices <- lapply(data_matrices, function(x) {
    x %>% dplyr::select(-one_of("patient_id"))
})

combined_df <- Reduce(function(x, y) plyr::join(x, y, type = "full"), data_matrices)
combined_df <- subset(combined_df, !is.na(cluster) & !condensed_id %in% c("7_BrnM"))

combined_mat <- subset(combined_df, select = -c(condensed_id, cluster))
rownames(combined_mat) <- combined_df$condensed_id
# ref_dendrogram <- prune(as.dendrogram(sample_heat$tree_row), '7_BrnM')

sample_order <- sample_heat$tree_row$labels[sample_heat$tree_row$order]
sample_order <- intersect(sample_order, rownames(combined_mat))

cluster_annotations <- subset(combined_df, select = c(condensed_id, cluster))

SIGS <- c("SV-3", "SNV-5", "SV-1", "SV-2", "SNV-1")
sig_annotations <- rownames_to_column(data.frame(props_sample_mat[, SIGS]), 
    var = "condensed_id")

## Take logarithms to ~variance stabilize
variant_count_annotations <- variant_sample %>% group_by_(.dots = "condensed_id") %>% 
    summarise(snv_count = log(sum(snv_count)), bkpt_count = log(sum(bkpt_count))) %>% 
    subset(select = c("condensed_id", "snv_count", "bkpt_count"))

row_annotations <- Reduce(plyr::join, list(cluster_annotations, molsubtypes, 
    sig_annotations, variant_count_annotations))
row_annotations <- row_annotations %>% column_to_rownames(var = "condensed_id")

combined_mat_scaled <- scale(combined_mat)

# hcs <- lapply(unique(combined_df$cluster), function(clust) { ids <-
# subset(combined_df, cluster == clust)$condensed_id dists <-
# dist(combined_mat_scaled[ids,], method = 'euclidean') #dists <-
# proxy::dist(combined_mat_scaled[ids,], method = function(x,y)
# pairwise_dist(x,y,method='canberra')) hclust(dists, method = 'ward.D') })
# min_height <- max(sapply(hcs, function(x) max(unique(cophenetic(x)))))*1.1
# hc <- Reduce(function(x,y) merge(as.dendrogram(x),as.dendrogram(y),height
# = min_height), hcs) hc <- as.dendrogram(ref_dendrogram) hc <-
# as.dendrogram(sample_heat2$tree_row) ha <-
# HeatmapAnnotation(row_annotations[rownames(combined_mat_scaled),],
# which='row') Heatmap(clip_values(combined_mat_scaled, 2, -2), cluster_rows
# = hc2, cluster_columns = TRUE, split = 2, clustering_method_columns =
# 'ward.D2') + ha

pheatmap(clip_values(combined_mat_scaled, 2, -2)[sample_order, ], cluster_rows = FALSE, 
    cluster_cols = TRUE, annotation_row = row_annotations, fontsize = 6)

What we can see is:

  • H-HRD cluster is relatively homogeneous.
  • Looks like there are two clusters within the H-FBI group – one characterized by high levels of immune activity (cytotoxicity, etc.) and one characterized by low levels.

This is a pretty significant finding.

Additionally, an interesting thing is that the patients/samples with the highest proportions of foldbacks – patients 2, 3, and 9 – are actually the ones with high immune response in the foldback group! Suggestive that perhaps foldback inversions can create neoepitopes. Of course very preliminary and low sample size though.

If you’re wondering why there’s no dendrogram on the vertical axis it’s because the plotting functions I’m currently using don’t allow for self-specified dendrograms. Trying to make one that lets me do so but it’s taking a bit of acrobatics and I’ve wasted a lot of time already …

To see ICGC validation, go to that section. I’ll add a link later …

TCR/BCR diversity

combined_df$patient_id <- map_id(combined_df$condensed_id, from = "condensed_id", 
    to = "patient_id", db_path)
combined_df_xcr <- melt(combined_df, id.vars = c("condensed_id", "patient_id", 
    "cluster"), measure.vars = XCR_VARS, variable.name = "type", value.name = "value")

pvals <- setNames(ddply(combined_df_xcr, .(type), function(x) {
    df <- as.data.frame(x)
    corres <- wilcox.test(value ~ cluster, df)
    
    pval <- corres$p.value
    eq <- substitute(italic(P) == p, list(p = format(pval, digits = 3)))
    return(as.character(as.expression(eq)))
}), c("type", "p.value"))


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

Celltypes and pathways

combined_df$patient_id <- map_id(combined_df$condensed_id, from = "condensed_id", 
    to = "patient_id", db_path)
combined_df_nanostring <- melt(combined_df, id.vars = c("condensed_id", "patient_id", 
    "cluster"), measure.vars = NANOSTRING_VARS, variable.name = "type", value.name = "value")

pvals <- setNames(ddply(combined_df_nanostring, .(type), function(x) {
    df <- as.data.frame(x)
    corres <- wilcox.test(value ~ cluster, df)
    
    pval <- corres$p.value
    eq <- substitute(italic(P) == p, list(p = format(pval, digits = 3)))
    return(as.character(as.expression(eq)))
}), c("type", "p.value"))


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

Ancestral-descendant level

clusters <- cutree(patient_heat$tree_row, NCLUST)
patient_clusts <- make_cluster_frame(clusters)

props_ad_merged_clusts <- join(props_ad_merged, patient_clusts)
patient_df <- unique(subset(props_ad_merged_clusts, select = selected_cols))

patient_df_melted <- melt(patient_df, id.vars = colnames(patient_df)[!colnames(patient_df) %in% 
    tiltypes], measure.vars = tiltypes, variable.name = "tiltype", value.name = "density")
pvals <- setNames(ddply(patient_df_melted, .(tiltype), function(x) {
    df <- as.data.frame(x)
    corres <- wilcox.test(density ~ cluster, df)
    
    pval <- corres$p.value
    eq <- substitute(italic(P) == p, list(p = format(pval, digits = 3)))
    return(as.character(as.expression(eq)))
}), c("tiltype", "p.value"))


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

Hence, the foldback group (cluster 2) has lower CD8+ and CD4+ densities than the HRD group, as expected.

A caveat is that p-values are computed with Wilcoxon, irrespective of patient number. We can’t really opt for a nonparametric nested ranks test because very few patients (4) are actually present in both clusters.

Ancestral analysis

Ancestral variants may have different properties from descendant variants.

Sample-specific

Here we’ll just naively allow for subclonal variants to be counted multiple times.

props_ad_merged$is_ancestral <- as.factor(props_ad_merged$is_ancestral)

pvals <- setNames(ddply(props_ad_merged, .(signature), function(x) {
    df <- as.data.frame(x)
    corres <- wilcox.test(proportion ~ is_ancestral, df)
    
    pval <- corres$p.value
    eq <- substitute(italic(P) == p, list(p = format(pval, digits = 3)))
    return(as.character(as.expression(eq)))
}), c("signature", "p.value"))

ggplot(props_ad_merged, aes(x = is_ancestral, y = proportion)) + geom_boxplot() + 
    geom_jitter(aes(colour = patient_id), position = position_jitter(width = 0.2, 
        height = 0)) + theme_bw() + theme_Publication() + scale_colour_manual(values = pal_patient) + 
    facet_wrap(~signature, scales = "free") + geom_text(data = pvals, aes(x = Inf, 
    y = Inf, label = p.value), hjust = 1.1, vjust = 1.5, size = 3, parse = TRUE)

P-values are uncorrected.

Unsurprisingly, ancestral samples have significantly more of SNV-4, the age signature. Insignificant, but they also have less SV-1, which is one of the BRCA’s (small deletions).

Union

Here we’ll actually take the union of subclonal variants for each patient so we don’t overcount.

props_patient_ad_merged$is_ancestral <- as.factor(props_patient_ad_merged$is_ancestral)

pvals <- setNames(ddply(props_patient_ad_merged, .(signature), function(x) {
    df <- as.data.frame(x)
    corres <- wilcox.test(proportion ~ is_ancestral, df, paired = TRUE)
    
    pval <- corres$p.value
    eq <- substitute(italic(P) == p, list(p = format(pval, digits = 3)))
    return(as.character(as.expression(eq)))
}), c("signature", "p.value"))

ggplot(props_patient_ad_merged, aes(x = is_ancestral, y = proportion)) + geom_boxplot() + 
    geom_jitter(aes(colour = patient_id), position = position_jitter(width = 0.2, 
        height = 0)) + theme_bw() + theme_Publication() + scale_colour_manual(values = pal_patient) + 
    facet_wrap(~signature, scales = "free") + geom_text(data = pvals, aes(x = Inf, 
    y = Inf, label = p.value), hjust = 1.1, vjust = 1.5, size = 3, parse = TRUE)

Aside from age and HRD which were described in McPherson et al., there’s additionally the SV-3 signature – a translocation signature. Implying that translocations are early (ancestral) events in HGSC – perhaps this is interesting? I have to do a literature search.

anc_desc_patient <- dcast(props_patient_ad_merged, formula = patient_id + signature ~ 
    is_ancestral, value.var = "proportion")

pvals <- setNames(ddply(anc_desc_patient, .(signature), function(x) {
    df <- as.data.frame(x)
    corres <- with(df, cor.test(`0`, `1`, method = "spearman"))
    
    pval <- corres$p.value
    eq <- substitute(italic(P) == p, list(p = format(pval, digits = 3)))
    return(as.character(as.expression(eq)))
}), c("signature", "p.value"))

ggplot(anc_desc_patient, aes(x = `1`, y = `0`)) + geom_point() + geom_point(aes(colour = patient_id)) + 
    theme_bw() + theme_Publication() + scale_colour_manual(values = pal_patient) + 
    facet_wrap(~signature, scales = "free") + geom_text(data = pvals, aes(x = Inf, 
    y = Inf, label = p.value), hjust = 1.1, vjust = 1.5, size = 3, parse = TRUE)

ICGC validation

Signatures

FBI subclusters

specimen_data <- fread(icgc_specimen_file)
icgc_subtypes <- fread(icgc_subtype_file)
icgc_expr_mat <- fread(icgc_expr_mat_file)
icgc_expr_df <- icgc_expr_mat %>% as.data.frame %>% column_to_rownames("icgc_donor_id") %>% 
    t %>% as.data.frame %>% rownames_to_column("Name")
icgc_celltype_matrix <- create_pathway_matrix(icgc_expr_df, labels, db_path, 
    convert_ids = FALSE)

icgc_immune <- rownames_to_column(as.data.frame(t(icgc_celltype_matrix)), var = "icgc_donor_id")

# cytotoxic_markers <- c('PRF1', 'GZMA', 'HLA-A', 'HLA-B', 'HLA-C', 'GZMK',
# 'GZMM', 'GZMH', 'GNLY', 'GZMB') icgc_immune <-
# as.data.frame(subset(icgc_expr_mat, select=c('icgc_donor_id',
# cytotoxic_markers))) for (i in 2:ncol(icgc_immune)) { icgc_immune[,i] <-
# log10(icgc_immune[,i]) }
props_ov_combined <- read_mutsig_output(mmctm_ov_combined_result_dir, samples, 
    external = TRUE)
props_ov_combined_prop <- props_ov_combined$prop

props_ov_combined_mat <- create_mutsig_matrix(props_ov_combined_prop, col = "proportion")

# props_ov_combined_mat_scaled <- as.data.frame(apply(props_ov_combined_mat,
# 2, function(x) { #x <- logit(x) (x-median(x,na.rm=TRUE))/mad(x,na.rm=TRUE)
# }))

props_ov_combined_mat_scaled <- scale(props_ov_combined_mat)

# props_ov_combined_mat_scaled <- clip_values(props_ov_combined_mat_scaled,
# 3, -3) clip_values(scale(props_ov_combined_mat), 2, -2)

ov_combined_heat <- pheatmap(props_ov_combined_mat_scaled, fontsize_row = 6, 
    clustering_distance_rows = "correlation", clustering_method = "ward.D2")

NCLUST <- 3

clusters <- cutree(ov_combined_heat$tree_row, NCLUST)
ov_combined_clusts <- make_cluster_frame(clusters)

colnames(icgc_immune) <- mapvalues(colnames(icgc_immune), "icgc_donor_id", "new_id")

data_matrices <- list(ov_combined_clusts, icgc_immune)
data_matrices <- lapply(data_matrices, function(x) {
    x %>% dplyr::select(-one_of("patient_id"))
})

combined_df <- Reduce(function(x, y) plyr::join(x, y, type = "inner"), data_matrices)
# combined_df <- subset(combined_df, cluster == 1)

combined_mat <- subset(combined_df, select = -c(new_id, cluster))
rownames(combined_mat) <- combined_df$new_id
sample_order <- ov_combined_heat$tree_row$labels[ov_combined_heat$tree_row$order]
sample_order <- intersect(sample_order, rownames(combined_mat))

cluster_annotations <- subset(combined_df, select = c(new_id, cluster))

SIGS <- c("SV-8", "SNV-1", "SV-4", "SV-7", "SV-5", "SNV-7", "SV-1")
sig_annotations <- rownames_to_column(data.frame(props_ov_combined_mat[, SIGS], 
    check.names = FALSE), var = "new_id")

subtype_annotations <- subset(icgc_subtypes, select = c("icgc_donor_id", "subtype", 
    "nmf_subtype"))
colnames(subtype_annotations)[1] <- "new_id"

row_annotations <- Reduce(plyr::join, list(cluster_annotations, sig_annotations, 
    subtype_annotations))
row_annotations <- row_annotations %>% column_to_rownames(var = "new_id")

select_rows <- rownames(subset(row_annotations, `SV-4` < 1))
notx <- subset(specimen_data, str_detect(specimen_type, "Primary") & specimen_donor_treatment_type == 
    "no treatment")$icgc_donor_id
sample_order <- intersect(sample_order, select_rows)
sample_order <- intersect(sample_order, notx)

# order_fbi <- order(row_annotations[rownames(combined_mat_scaled),]$`SV-8`)

# combined_mat_scaled <- scale(combined_mat)
combined_mat_scaled <- as.data.frame(apply(combined_mat, 2, function(x) (x - 
    median(x, na.rm = TRUE))/mad(x, na.rm = TRUE)))

combined_mat_scaled <- combined_mat_scaled[sample_order, ]
other_mat <- props_ov_combined_mat_scaled[sample_order, ]

pheatmap(clip_values(cbind(combined_mat_scaled, other_mat), 2, -2), cluster_rows = FALSE, 
    cluster_cols = TRUE, annotation_row = row_annotations, fontsize = 6, clustering_distance_cols = "correlation", 
    clustering_method = "ward.D2")

# rowmean <- rowMeans(combined_mat_scaled) a <- merge(row_annotations,
# as.data.frame(rowmean), by='row.names') ggplot(subset(a, cluster != 1),
# aes(x=rowmean > median(rowmean), y = `SNV-7`)) + geom_boxplot() +
# theme_bw()
clusters <- setNames(cluster_annotations, c("icgc_donor_id", "cluster"))

Differential expression

icgc_expr_melted <- fread(icgc_expr_raw_file)

Read 0.0% of 3709596 rows
Read 11.6% of 3709596 rows
Read 23.5% of 3709596 rows
Read 35.3% of 3709596 rows
Read 47.4% of 3709596 rows
Read 59.3% of 3709596 rows
Read 71.2% of 3709596 rows
Read 83.0% of 3709596 rows
Read 95.2% of 3709596 rows
Read 3709596 rows and 15 (of 15) columns from 0.484 GB file in 00:00:16
icgc_expr_casted_matrix <- expression_df_to_matrix(icgc_expr_melted, summarize_over = "icgc_donor_id", 
    measure_var = "raw_read_count")

icgc_counts <- icgc_expr_casted_matrix %>% column_to_rownames(var = "icgc_donor_id")
icgc_counts_filtered <- icgc_counts[clusters$icgc_donor_id, ]
icgc_counts_filtered <- icgc_counts_filtered %>% t %>% as.data.frame

dge <- DGEList(counts = icgc_counts_filtered)
dge <- calcNormFactors(dge)

library_sizes <- colSums(icgc_counts_filtered)
library_sizes
  DO46325   DO46327   DO46328   DO46329   DO46330   DO46331   DO46332 
 78792862 106156028 146714663 100782140  66648443  94187980 128035792 
  DO46333   DO46334   DO46336   DO46338   DO46340   DO46342   DO46344 
 70763442  87776088 106659467  67264480  72586319  91076278  97575765 
  DO46346   DO46350   DO46352   DO46354   DO46356   DO46358   DO46360 
 78219082  65895620  95169618  92438598  46265120  90175214  96687938 
  DO46362   DO46364   DO46366   DO46368   DO46370   DO46372   DO46374 
 95856331 101399457  90792124  97051216 100193800  92056106 116039147 
  DO46376   DO46378   DO46380   DO46382   DO46384   DO46386   DO46388 
 81664713 159721278  90029262  80192737  80784914  88436986  95577226 
  DO46390   DO46392   DO46394   DO46396   DO46398   DO46400   DO46402 
 88612126  91723742  91209587  87119354  79391516  93620832  94446982 
  DO46404   DO46408   DO46412   DO46448   DO46493   DO46551   DO46560 
 97746414  91333342  95814730  88596500 123302138  84121097 109112881 
  DO46561   DO46566   DO46568   DO46571   DO46576   DO46581   DO46586 
 99873236  79993818  95909367  75957744  91988944  98993304  95877151 
  DO46588   DO46591   DO46597   DO46602   DO46606   DO46611 
 78452166 113011642  94472903  96109428  98892638  61422278 
max(library_sizes)/min(library_sizes)
[1] 3.452304
design <- model.matrix(~0 + factor(clusters$cluster))
colnames(design) <- paste0("clust", 1:NCLUST)
contrast.matrix <- makeContrasts(clust1 - clust3, clust2 - (clust1 + clust3)/2, 
    clust2 - clust1, levels = design)

limma-trend

We straddle close to threshold for doing this but let’s do it anyways.

fit_trend <- rnaseq_DE_initial_fit(dge, design, type = "limma_trend")
fit_trend_contrasts <- contrasts.fit(fit_trend, contrast.matrix)
fit_trend_contrasts <- eBayes(fit_trend_contrasts)

hrd_fbi_genes <- topTable(fit_trend_contrasts, coef = "clust2 - (clust1 + clust3)/2", 
    adjust.method = "BH", number = Inf, sort = "p")
fbi_immune_genes <- topTable(fit_trend_contrasts, coef = "clust1 - clust3", 
    number = Inf, sort = "p")
hrd_fbi_highimmune_genes <- topTable(fit_trend_contrasts, coef = "clust2 - clust1", 
    number = Inf, sort = "p")
hrd_fbi_pathways <- pathway_analysis(hrd_fbi_genes, gs_type = "go")
fbi_immune_pathways <- pathway_analysis(fbi_immune_genes, gs_type = "go")
hrd_fbi_highimmune_pathways <- pathway_analysis(hrd_fbi_highimmune_genes, gs_type = "go")
HRD vs. FBI samples (clust2 vs. clust1+clust3)
datatable(hrd_fbi_pathways, extensions = "Buttons", options = list(pageLength = 5, 
    scrollX = TRUE, dom = "Bfrtip", buttons = c("copy", "csv", "excel", "pdf", 
        "print")))
High immune FBI vs. low immune FBI (clust1 vs. clust3)
datatable(fbi_immune_pathways, extensions = "Buttons", options = list(pageLength = 5, 
    scrollX = TRUE, dom = "Bfrtip", buttons = c("copy", "csv", "excel", "pdf", 
        "print")))
HRD vs. high immune FBI (clust2 vs. clust1)
datatable(hrd_fbi_highimmune_pathways, extensions = "Buttons", options = list(pageLength = 5, 
    scrollX = TRUE, dom = "Bfrtip", buttons = c("copy", "csv", "excel", "pdf", 
        "print")))

voom

We have greater than 3-fold variability in library size, so we’re better off using the voom method for DE analysis.

fit_voom <- rnaseq_DE_initial_fit(dge, design, type = "voom")
fit_voom_contrasts <- contrasts.fit(fit_voom, contrast.matrix)
fit_voom_contrasts <- eBayes(fit_voom_contrasts)

hrd_fbi_genes <- topTable(fit_voom_contrasts, coef = "clust2 - (clust1 + clust3)/2", 
    adjust.method = "BH", number = Inf, sort = "p")
fbi_immune_genes <- topTable(fit_voom_contrasts, coef = "clust1 - clust3", number = Inf, 
    sort = "p")
hrd_fbi_highimmune_genes <- topTable(fit_voom_contrasts, coef = "clust2 - clust1", 
    number = Inf, sort = "p")
hrd_fbi_pathways <- pathway_analysis(hrd_fbi_genes, gs_type = "kegg")
fbi_immune_pathways <- pathway_analysis(fbi_immune_genes, gs_type = "kegg")
hrd_fbi_highimmune_pathways <- pathway_analysis(hrd_fbi_highimmune_genes, gs_type = "kegg")
# pv.out.list <- sapply(path.ids2, function(pid) pathview( gene.data =
# exp.fc, pathway.id = pid, species = 'hsa', out.suffix=out.suffix))

Note: BRCA1, BRCA2, and RPA are also downregulated in the HRD group – the entire HR-associated gene cluster isn’t significantly downregulated though.

One of the key reasons why the entire HR pathway isn’t significantly downregulated is because PolQ is part of it and PolQ is upregulated. Of note, PolQ promotes MMEJ, providing further evidence for MMEJ activity in FBI tumours.

HRD vs. FBI samples (clust2 vs. clust1+clust3)
datatable(hrd_fbi_pathways, extensions = "Buttons", options = list(pageLength = 5, 
    scrollX = TRUE, dom = "Bfrtip", buttons = c("copy", "csv", "excel", "pdf", 
        "print")))
High immune FBI vs. low immune FBI (clust1 vs. clust3)
datatable(fbi_immune_pathways, extensions = "Buttons", options = list(pageLength = 5, 
    scrollX = TRUE, dom = "Bfrtip", buttons = c("copy", "csv", "excel", "pdf", 
        "print")))
HRD vs. high immune FBI (clust2 vs. clust1)
datatable(hrd_fbi_highimmune_pathways, extensions = "Buttons", options = list(pageLength = 5, 
    scrollX = TRUE, dom = "Bfrtip", buttons = c("copy", "csv", "excel", "pdf", 
        "print")))

Some DNA repair genes

v <- voom(icgc_counts_filtered, design, normalize = "quantile", plot = FALSE)
icgc_expr_df_wide <- v$E %>% t %>% as.data.frame %>% rownames_to_column("icgc_donor_id")

icgc_expr_df_melted <- reshape2::melt(icgc_expr_df_wide, id.vars = c("icgc_donor_id"), 
    measure.vars = colnames(icgc_expr_df_wide)[2:ncol(icgc_expr_df_wide)], variable.name = "Name", 
    value.name = "expression")

icgc_expr_df_melted_labeled <- plyr::join(icgc_expr_df_melted, clusters)

REPAIR_GENES <- c("BRCA1", "BRCA2", "POLQ", "FEN1", "XRCC1", "PARP1", "LIG3")

icgc_expr_df_melted_labeled_filtered <- subset(icgc_expr_df_melted_labeled, 
    Name %in% REPAIR_GENES)

ggplot(subset(icgc_expr_df_melted_labeled_filtered, !is.na(cluster)), aes(x = cluster, 
    y = expression)) + geom_boxplot() + geom_jitter(position = position_jitter(width = 0.2, 
    height = 0)) + theme_bw() + theme_Publication() + facet_wrap(~Name, scales = "free")

Survival

icgc_clinical <- read_donor_specimen_survival(icgc_donor_file, icgc_specimen_file)

icgc_clinical_labeled <- plyr::join(clusters, icgc_clinical)

icgc_clinical_labeled$SurvObj <- with(icgc_clinical_labeled, Surv(donor_survival_time, 
    donor_vital_status == "deceased"))

Survival by FBI status …

simple_survival_analysis(SurvObj ~ cluster != 2, data = icgc_clinical_labeled)

Call:
survival::survdiff(formula = formula, data = data)

                    N Observed Expected (O-E)^2/E (O-E)^2/V
cluster != 2=FALSE 27       25     29.3     0.641      1.34
cluster != 2=TRUE  38       34     29.7     0.634      1.34

 Chisq= 1.3  on 1 degrees of freedom, p= 0.246 
simple_survival_analysis(SurvObj ~ cluster, data = icgc_clinical_labeled)

Call:
survival::survdiff(formula = formula, data = data)

           N Observed Expected (O-E)^2/E (O-E)^2/V
cluster=1 21       18     15.8     0.293     0.413
cluster=2 27       25     29.3     0.641     1.344
cluster=3 17       16     13.8     0.344     0.473

 Chisq= 1.3  on 2 degrees of freedom, p= 0.509 

TCGA validation

While we can’t directly do mutation signature analysis with MMCTM on exome data (I suppose this is theoretically possible but we’d probably be restricted in the types of events we can call, like long SVs), we can look at the FBI-HLAMP finding from Yikan’s paper and see if that lines up with immune signatures.

tcga_expr_mat <- read_tcga_exprs(tcga_expr_mat_file)

Read 0.0% of 570 rows
Read 570 rows and 12497 (of 12497) columns from 0.121 GB file in 00:00:05
tcga_ov_annotation <- fread(tcga_ov_annotation_file)
# tcga_fbi_hlamp_proportions <- fread(tcga_fbi_hlamp_proportions_file)
tcga_expr_df <- tcga_expr_mat %>% as.data.frame %>% column_to_rownames("tcga_sample_id") %>% 
    t %>% as.data.frame %>% rownames_to_column("Name")
tcga_celltype_matrix <- create_pathway_matrix(tcga_expr_df, labels, db_path, 
    convert_ids = FALSE)

tcga_immune <- rownames_to_column(as.data.frame(t(tcga_celltype_matrix)), var = "tcga_sample_id")
mat <- tcga_immune %>% column_to_rownames("tcga_sample_id") %>% scale  #%>% clip_values(2, -2)
row_annotations <- tcga_ov_annotation %>% as.data.frame %>% column_to_rownames("tcga_sample_id")

tcgaheat <- pheatmap(mat, annotation_row = row_annotations, cluster_rows = TRUE, 
    cluster_cols = TRUE, clustering_method = "ward.D", show_rownames = FALSE)

# tcga_clusters <-
# rownames_to_column(data.frame(cluster=factor(cutree(tcgaheat$tree_row,
# 2))), var = 'tcga_sample_id')

col <- "Cytotoxicity"
tcga_clusters <- rownames_to_column(data.frame(cluster = mat[, col] > median(mat[, 
    col])), "tcga_sample_id") %>% mutate(cluster = as.factor(as.numeric(cluster)))

# row_annotations <- plyr::join(tcga_ov_annotation, tcga_clusters) %>%
# as.data.frame %>% column_to_rownames('tcga_sample_id') pheatmap(mat,
# annotation_row = row_annotations, cluster_rows = TRUE, cluster_cols =
# TRUE, clustering_method = 'ward.D', show_rownames = FALSE)
df_merged <- plyr::join(tcga_immune, tcga_ov_annotation)

expr_measure_vars <- colnames(tcga_immune)[2:ncol(tcga_immune)]

df_merged_melted <- melt(df_merged, id.vars = c("tcga_sample_id", "Subgroup", 
    "MolecularSubtype", "BRCA.status"), measure.vars = expr_measure_vars, variable.name = "Measure", 
    value.name = "Expression")
pvals <- setNames(ddply(subset(df_merged_melted, !is.na(Subgroup)), .(Measure), 
    function(x) {
        df <- as.data.frame(x)
        testres <- kruskal.test(Expression ~ factor(Subgroup), data = df)
        
        pval <- testres$p.value
        eq <- substitute(italic(P) == p, list(p = format(pval, digits = 3)))
        return(as.character(as.expression(eq)))
    }), c("Measure", "p.value"))

ggplot(subset(df_merged_melted, !is.na(Subgroup)), aes(x = Subgroup, y = Expression)) + 
    geom_boxplot() + theme_bw() + facet_wrap(~Measure, scales = "free", ncol = 3) + 
    theme_Publication() + geom_text(data = pvals, aes(x = Inf, y = Inf, label = p.value), 
    hjust = 1.1, vjust = 1.5, size = 3, parse = TRUE)

P-values are uncorrected. So after correction, we aren’t going to get anything significant.

In conclusion, the immune subgroups constitute a new subgrouping independent of FBI-HLAMP.

I’ve also done correlation testing using the logR values that Yikan’s provided me (rather than the discrete groupings of no AMP, FBI-AMP low, and FBI-AMP high), but the correlations are very poor (rho values of ~ -0.05 or so, p-values of >=0.3).

Survival

tcga_clinical <- read_tcga_clinical(tcga_donor_file, type = "synapse2", filter = TRUE, 
    unique = FALSE)

tcga_clinical_labeled <- Reduce(function(x, y) plyr::join(x, y, type = "full"), 
    list(tcga_clusters, tcga_clinical, tcga_ov_annotation))

tcga_clinical_labeled$SurvObj <- with(tcga_clinical_labeled, Surv(ifelse(vital_status == 
    "Dead", death_days_to, last_contact_days_to), vital_status == "Dead"))

# tcga_clinical_labeled <- subset(tcga_clinical_labeled,
# additional_drug_therapy == 'NO' & additional_immuno_therapy == 'NO' &
# additional_pharmaceutical_therapy == 'NO' & targeted_molecular_therapy ==
# 'NO' & radiation_therapy == 'NO') tcga_clinical_labeled <-
# subset(tcga_clinical_labeled, str_detect(tumor_stage, '^III'))

FBI-AMP colocalization

Let’s first stratify by Yikan’s groupings:

simple_survival_analysis(SurvObj ~ Subgroup, data = tcga_clinical_labeled)

Call:
survival::survdiff(formula = formula, data = data)

n=430, 147 observations deleted due to missingness.

                        N Observed Expected (O-E)^2/E (O-E)^2/V
Subgroup=FBI-AMP High 173       86     67.7      4.94      7.43
Subgroup=FBI-AMP Low  182       80     92.1      1.60      2.87
Subgroup=No AMP        75       43     49.2      0.77      1.03

 Chisq= 7.4  on 2 degrees of freedom, p= 0.0243 

Note: The logrank p-values in the paper are incorrect, the one’s I’ve computed are the right ones. In the paper, the death days from the dead patients were not correctly combined into the computation, so p-values were computed without the dead patients.

Immune activity

We’ll now stratify by cluster, a variable that indicates whether or not we’re above median in terms of Cytotoxicity. In other words, 1 means we’re high immune, and 0 means we’re low immune.

simple_survival_analysis(SurvObj ~ cluster, data = tcga_clinical_labeled)

Call:
survival::survdiff(formula = formula, data = data)

n=562, 15 observations deleted due to missingness.

            N Observed Expected (O-E)^2/E (O-E)^2/V
cluster=0 280      162      148      1.38      2.79
cluster=1 282      132      146      1.39      2.79

 Chisq= 2.8  on 1 degrees of freedom, p= 0.095 

Low immune tumours trend towards poor survival but this is not significant at all.

Now let’s try by treating immune activity as a continuous variable, controlling for other covariates:

tcga_immune_continuous <- tcga_celltype_matrix %>% t %>% as.data.frame %>% rownames_to_column("tcga_sample_id")

tcga_clinical_labeled_continuous <- plyr::join(tcga_clinical_labeled, tcga_immune_continuous)

coxph(SurvObj ~ Cytotoxicity + age_at_initial_pathologic_diagnosis + tumor_grade + 
    str_extract(tumor_stage, "[IV]+") + additional_chemo_therapy + additional_drug_therapy + 
    additional_immuno_therapy, tcga_clinical_labeled_continuous)
Call:
coxph(formula = SurvObj ~ Cytotoxicity + age_at_initial_pathologic_diagnosis + 
    tumor_grade + str_extract(tumor_stage, "[IV]+") + additional_chemo_therapy + 
    additional_drug_therapy + additional_immuno_therapy, data = tcga_clinical_labeled_continuous)

                                              coef exp(coef)  se(coef)
Cytotoxicity                             -1.68e-01  8.45e-01  8.25e-02
age_at_initial_pathologic_diagnosis       1.95e-02  1.02e+00  5.50e-03
tumor_gradeG1                            -4.72e-01  6.24e-01  1.27e+00
tumor_gradeG2                            -2.99e-01  7.42e-01  1.06e+00
tumor_gradeG3                            -1.19e-02  9.88e-01  1.06e+00
tumor_gradeG4                             3.76e-01  1.46e+00  1.45e+00
tumor_gradeGB                             1.14e-02  1.01e+00  1.45e+00
tumor_gradeGX                             3.20e-01  1.38e+00  1.16e+00
str_extract(tumor_stage, "[IV]+")II       1.12e-01  1.12e+00  7.00e-01
str_extract(tumor_stage, "[IV]+")III      9.32e-01  2.54e+00  5.91e-01
str_extract(tumor_stage, "[IV]+")IV       1.19e+00  3.29e+00  6.07e-01
additional_chemo_therapy[Not Available]  -9.30e-03  9.91e-01  3.53e+03
additional_chemo_therapyNO               -1.14e+00  3.19e-01  3.53e+03
additional_chemo_therapyYES              -9.73e-01  3.78e-01  3.53e+03
additional_drug_therapy[Not Available]   -1.49e+01  3.44e-07  2.42e+03
additional_drug_therapy[Pending]          5.16e-01  1.68e+00  3.53e+03
additional_drug_therapyNO                 4.78e-01  1.61e+00  3.53e+03
additional_drug_therapyYES               -1.28e-02  9.87e-01  3.53e+03
additional_immuno_therapy[Not Available]  1.44e+01  1.87e+06  2.58e+03
additional_immuno_therapy[Pending]       -1.40e+01  8.52e-07  4.19e+03
additional_immuno_therapyNO              -1.95e-01  8.23e-01  3.30e-01
additional_immuno_therapyYES                    NA        NA  0.00e+00
                                             z       p
Cytotoxicity                             -2.04 0.04158
age_at_initial_pathologic_diagnosis       3.55 0.00038
tumor_gradeG1                            -0.37 0.71118
tumor_gradeG2                            -0.28 0.77908
tumor_gradeG3                            -0.01 0.99097
tumor_gradeG4                             0.26 0.79603
tumor_gradeGB                             0.01 0.99372
tumor_gradeGX                             0.28 0.78225
str_extract(tumor_stage, "[IV]+")II       0.16 0.87332
str_extract(tumor_stage, "[IV]+")III      1.58 0.11449
str_extract(tumor_stage, "[IV]+")IV       1.96 0.04961
additional_chemo_therapy[Not Available]   0.00 1.00000
additional_chemo_therapyNO                0.00 0.99974
additional_chemo_therapyYES               0.00 0.99978
additional_drug_therapy[Not Available]   -0.01 0.99509
additional_drug_therapy[Pending]          0.00 0.99988
additional_drug_therapyNO                 0.00 0.99989
additional_drug_therapyYES                0.00 1.00000
additional_immuno_therapy[Not Available]  0.01 0.99553
additional_immuno_therapy[Pending]        0.00 0.99734
additional_immuno_therapyNO              -0.59 0.55541
additional_immuno_therapyYES                NA      NA

Likelihood ratio test=46.8  on 21 df, p=0.00101
n= 559, number of events= 292 
   (18 observations deleted due to missingness)

Indeed, immune activity is significantly associated with prolonged survival (negative coefficient = fewer death events).

Stratification by foldback-AMP status

fbi_high <- subset(tcga_clinical_labeled, Subgroup == "FBI-AMP High")
fbi_low <- subset(tcga_clinical_labeled, Subgroup == "FBI-AMP Low")
fbi_no <- subset(tcga_clinical_labeled, Subgroup == "No AMP")
fbi_nothigh <- subset(tcga_clinical_labeled, Subgroup %in% c("FBI-AMP Low", 
    "No AMP"))
simple_survival_analysis(SurvObj ~ cluster, data = fbi_high)

Call:
survival::survdiff(formula = formula, data = data)

n=172, 2 observations deleted due to missingness.

           N Observed Expected (O-E)^2/E (O-E)^2/V
cluster=0 90       45     48.9     0.310     0.739
cluster=1 82       40     36.1     0.419     0.739

 Chisq= 0.7  on 1 degrees of freedom, p= 0.39 
simple_survival_analysis(SurvObj ~ cluster, data = fbi_low)

Call:
survival::survdiff(formula = formula, data = data)

n=181, 5 observations deleted due to missingness.

           N Observed Expected (O-E)^2/E (O-E)^2/V
cluster=0 91       42     43.5    0.0514     0.116
cluster=1 90       37     35.5    0.0630     0.116

 Chisq= 0.1  on 1 degrees of freedom, p= 0.733 
simple_survival_analysis(SurvObj ~ cluster, data = fbi_no)

Call:
survival::survdiff(formula = formula, data = data)

           N Observed Expected (O-E)^2/E (O-E)^2/V
cluster=0 34       24     18.2      1.84      3.23
cluster=1 41       19     24.8      1.35      3.23

 Chisq= 3.2  on 1 degrees of freedom, p= 0.0724 

What’s curious about this is that it seems only the no AMP group gets any benefit from having a high immune response. So there’s no benefit within the foldback group of high/low immune response.

simple_survival_analysis(SurvObj ~ cluster, data = fbi_nothigh)

Call:
survival::survdiff(formula = formula, data = data)

n=256, 5 observations deleted due to missingness.

            N Observed Expected (O-E)^2/E (O-E)^2/V
cluster=0 125       66     61.9     0.275     0.559
cluster=1 131       56     60.1     0.283     0.559

 Chisq= 0.6  on 1 degrees of freedom, p= 0.455 

Stratification by immune activity

immune_low <- subset(tcga_clinical_labeled, cluster == 0)
immune_high <- subset(tcga_clinical_labeled, cluster == 1)
simple_survival_analysis(SurvObj ~ Subgroup, data = immune_high)

Call:
survival::survdiff(formula = formula, data = data)

n=213, 72 observations deleted due to missingness.

                       N Observed Expected (O-E)^2/E (O-E)^2/V
Subgroup=FBI-AMP High 82       40     28.1     5.005     7.303
Subgroup=FBI-AMP Low  90       37     40.5     0.299     0.531
Subgroup=No AMP       41       19     27.4     2.569     3.935

 Chisq= 8.3  on 2 degrees of freedom, p= 0.0155 
simple_survival_analysis(SurvObj ~ Subgroup, data = immune_low)

Call:
survival::survdiff(formula = formula, data = data)

n=215, 70 observations deleted due to missingness.

                       N Observed Expected (O-E)^2/E (O-E)^2/V
Subgroup=FBI-AMP High 90       45     38.6     1.060     1.651
Subgroup=FBI-AMP Low  91       42     50.8     1.535     2.843
Subgroup=No AMP       34       24     21.6     0.275     0.345

 Chisq= 2.9  on 2 degrees of freedom, p= 0.236 

Likewise, the only benefit of being non-foldback is derived from the immune-high group – there’s no difference between being foldback or not if you’re in the low immune group.

Multivariate models

simple_survival_analysis(SurvObj ~ Subgroup + cluster, data = tcga_clinical_labeled)

Call:
survival::survdiff(formula = formula, data = data)

n=428, 149 observations deleted due to missingness.

                                  N Observed Expected (O-E)^2/E (O-E)^2/V
Subgroup=FBI-AMP High, cluster=0 90       45     38.4     1.118     1.380
Subgroup=FBI-AMP High, cluster=1 82       40     28.5     4.624     5.436
Subgroup=FBI-AMP Low, cluster=0  91       42     50.1     1.307     1.730
Subgroup=FBI-AMP Low, cluster=1  90       37     41.1     0.415     0.523
Subgroup=No AMP, cluster=0       34       24     21.0     0.444     0.496
Subgroup=No AMP, cluster=1       41       19     27.9     2.819     3.331

 Chisq= 10.9  on 5 degrees of freedom, p= 0.0525 

We’ll next combined FBI-AMP low and No AMP into a single category called FBI-NotHigh.

tcga_clinical_labeled$newgroup <- ifelse(tcga_clinical_labeled$Subgroup == "FBI-AMP High", 
    "FBI-High", "FBI-NotHigh")

simple_survival_analysis(SurvObj ~ newgroup + cluster, data = tcga_clinical_labeled)

Call:
survival::survdiff(formula = formula, data = data)

n=428, 149 observations deleted due to missingness.

                                  N Observed Expected (O-E)^2/E (O-E)^2/V
newgroup=FBI-High, cluster=0     90       45     38.4     1.118     1.380
newgroup=FBI-High, cluster=1     82       40     28.5     4.624     5.436
newgroup=FBI-NotHigh, cluster=0 125       66     71.0     0.358     0.549
newgroup=FBI-NotHigh, cluster=1 131       56     69.0     2.448     3.686

 Chisq= 8.7  on 3 degrees of freedom, p= 0.0337 

Let’s also do Cox proportional hazards models:

mod1 <- coxph(SurvObj ~ Cytotoxicity + Subgroup + age_at_initial_pathologic_diagnosis + 
    tumor_grade + str_extract(tumor_stage, "[IV]+") + additional_chemo_therapy + 
    additional_drug_therapy + additional_immuno_therapy, tcga_clinical_labeled_continuous)
anova(mod1)
mod2 <- coxph(SurvObj ~ Cytotoxicity + Subgroup + age_at_initial_pathologic_diagnosis, 
    tcga_clinical_labeled_continuous)
anova(mod2)
mod3 <- coxph(SurvObj ~ Cytotoxicity + (Subgroup == "FBI-AMP High") + age_at_initial_pathologic_diagnosis, 
    subset(tcga_clinical_labeled_continuous, !is.na(Subgroup)))
anova(mod3)

So the effects of immune activity and FBI/HRD status are collinear.

Final run

Instead of overwriting the previous, this is its own section since the underlying implementation has changed substantially.

variant_table <- read_variant_file(master_variant_file, db_path)
breakpoint_table <- read_variant_file(master_breakpoint_file, db_path)

sig_results <- read_signature_files(mmctm_final_patient_dir, variant_table, 
    breakpoint_table, db_path)

sig_results_snv <- subset(sig_results$snv, is_present == 1)
sig_results_sv <- subset(sig_results$sv, is_present == 1)
sig_results_nonith <- sig_results$nonith %>% as.data.frame %>% column_to_rownames("signature") %>% 
    t %>% as.data.frame %>% rownames_to_column("condensed_id")

signature_labels <- str_extract(c(colnames(sig_results_snv), colnames(sig_results_sv)), 
    "SN?V-[0-9]+")
signature_labels <- signature_labels[!is.na(signature_labels)]
summarize_signature_proportions <- function(x, by = c("condensed_id", "patient_id"), 
    signature_labels, report_count = FALSE) {
    props <- subset(x, select = c(by, colnames(x)[colnames(x) %in% signature_labels])) %>% 
        group_by_(.dots = by) %>% summarise_each(funs(sum))
    
    if (report_count) {
        counts <- subset(x, select = c(by, colnames(x)[colnames(x) %in% signature_labels])) %>% 
            group_by_(.dots = by) %>% summarise(n = n())
        props <- plyr::join(props %>% as.data.frame, counts %>% as.data.frame)
    }
    sums <- rowSums(subset(props, select = colnames(props[colnames(props) %in% 
        signature_labels])))
    sigs <- intersect(signature_labels, colnames(props))
    for (sig in sigs) {
        props[, sig] <- props[, sig]/sums
    }
    return(props)
}

Signatures

Sample signature proportions

These proportions won’t be exactly equal to the topic proportions that the model outputs (they are variational estimates), but we’re actually doing pretty darn well.

TODO: Make a QC plot. From a glance it seems that proportion error is usually within 1-5% relative error.

sample_props_snv <- summarize_signature_proportions(sig_results_snv, by = c("condensed_id", 
    "patient_id"), signature_labels)
sample_props_sv <- summarize_signature_proportions(sig_results_sv, by = c("condensed_id", 
    "patient_id"), signature_labels)

patient_props_snv <- sig_results_snv %>% subset(select = c("patient_id", "chrom", 
    "coord", "ref", "alt", intersect(signature_labels, colnames(sig_results_snv)))) %>% 
    unique %>% summarize_signature_proportions(by = c("patient_id"), signature_labels)
patient_props_sv <- sig_results_sv %>% subset(select = c("patient_id", "prediction_id", 
    intersect(signature_labels, colnames(sig_results_sv)))) %>% unique %>% summarize_signature_proportions(by = c("patient_id"), 
    signature_labels)
sample_props <- plyr::join(sample_props_snv %>% as.data.frame, sample_props_sv %>% 
    as.data.frame)
sample_props <- rbind.fill(sample_props, sig_results_nonith)

sample_props_mat <- sample_props %>% column_to_rownames("condensed_id") %>% 
    subset(select = c(colnames(sample_props)[colnames(sample_props) %in% signature_labels]))

sample_props_mat_scaled <- scale(sample_props_mat)
# [,c('SNV-2', 'SNV-5', 'SV-3', 'SV-6', 'SV-8', 'SV-1', 'SV-7', 'SV-5',
# 'SNV-3')]
sample_props_heat <- pheatmap(sample_props_mat_scaled, fontsize_row = 5, clustering_method = "ward.D2")

patient_props <- plyr::join(patient_props_snv %>% as.data.frame, patient_props_sv %>% 
    as.data.frame)
patient_props <- rbind.fill(patient_props, sig_results_nonith %>% plyr::rename(c(condensed_id = "patient_id")))

patient_props_mat <- patient_props %>% column_to_rownames("patient_id") %>% 
    subset(select = c(colnames(patient_props)[colnames(patient_props) %in% signature_labels]))

patient_props_mat_scaled <- scale(patient_props_mat)
# [,c('SNV-2', 'SNV-5', 'SV-3', 'SV-6', 'SV-8', 'SV-1', 'SV-7', 'SV-5',
# 'SNV-3')]
patient_props_heat <- pheatmap(patient_props_mat_scaled, fontsize_row = 5, clustering_method = "ward.D2", 
    clustering_distance_rows = "correlation")

ITH cohort

sample_clusts <- as.data.frame(cutree(sample_props_heat$tree_row, 4)) %>% setNames(c("cluster")) %>% 
    rownames_to_column("condensed_id")

ihc_stabilize_subset <- stabilize_ihc_variances(ihc_table_slide, ihc_table, 
    tiltypes)

data_matrices <- list(sample_clusts, ihc_stabilize_subset, xcr_diversity, celltype_df, 
    pathway_df, proportion_subclonality_subset)
data_matrices <- lapply(data_matrices, function(x) {
    x %>% dplyr::select(-one_of("patient_id"))
})

combined_df <- Reduce(function(x, y) plyr::join(x, y, type = "full"), data_matrices)
combined_df <- subset(combined_df, !is.na(cluster) & !condensed_id %in% c("7_BrnM"))
combined_df <- subset(combined_df, condensed_id %in% subset(samples, project_code == 
    "ITH")$condensed_id)

combined_mat <- subset(combined_df, select = -c(condensed_id, cluster))
rownames(combined_mat) <- combined_df$condensed_id
# ref_dendrogram <- prune(as.dendrogram(sample_heat$tree_row), '7_BrnM')

sample_order <- sample_props_heat$tree_row$labels[sample_props_heat$tree_row$order]
sample_order <- intersect(sample_order, rownames(combined_mat))

cluster_annotations <- subset(combined_df, select = c(condensed_id, cluster))

SIGS <- c("SV-3", "SNV-5", "SV-6", "SV-8", "SNV-2")
sig_annotations <- rownames_to_column(data.frame(sample_props_mat[, SIGS], check.names = FALSE), 
    var = "condensed_id")

## Take logarithms to ~variance stabilize
variant_count_annotations <- variant_sample %>% group_by_(.dots = "condensed_id") %>% 
    summarise(snv_count = log(sum(snv_count)), bkpt_count = log(sum(bkpt_count))) %>% 
    subset(select = c("condensed_id", "snv_count", "bkpt_count"))

row_annotations <- Reduce(plyr::join, list(cluster_annotations, molsubtypes, 
    sig_annotations, variant_count_annotations))
row_annotations <- row_annotations %>% column_to_rownames(var = "condensed_id")
row_annotations_ith <- row_annotations

combined_mat_scaled <- scale(combined_mat)

pheatmap(clip_values(combined_mat_scaled, 2, -2)[sample_order, ], cluster_rows = FALSE, 
    cluster_cols = TRUE, annotation_row = row_annotations, fontsize = 6)

combined_mat_scaled_ith <- combined_mat_scaled

ICGC validation

icgc_fbi_status <- fread(wang_icgc_fbi_status_file)
colnames(icgc_fbi_status) <- mapvalues(colnames(icgc_fbi_status), from = c("Case_ID"), 
    to = c("condensed_id"))
colnames(icgc_immune) <- mapvalues(colnames(icgc_immune), "new_id", "condensed_id")

data_matrices <- list(sample_clusts, icgc_immune)
data_matrices <- lapply(data_matrices, function(x) {
    x %>% dplyr::select(-one_of("patient_id"))
})

combined_df <- Reduce(function(x, y) plyr::join(x, y, type = "inner"), data_matrices)
# combined_df <- subset(combined_df, cluster == 1)

combined_mat <- subset(combined_df, select = -c(condensed_id, cluster))
rownames(combined_mat) <- combined_df$condensed_id
sample_order <- sample_props_heat$tree_row$labels[sample_props_heat$tree_row$order]
sample_order <- intersect(sample_order, rownames(combined_mat))

cluster_annotations <- subset(combined_df, select = c(condensed_id, cluster))

SIGS <- c("SV-3", "SNV-5", "SV-6", "SV-8", "SNV-2")
sig_annotations <- rownames_to_column(data.frame(sample_props_mat[, SIGS], check.names = FALSE), 
    var = "condensed_id")

subtype_annotations <- subset(icgc_subtypes, select = c("icgc_donor_id", "subtype", 
    "nmf_subtype"))
colnames(subtype_annotations)[1] <- "condensed_id"

icgc_fbi_annotations <- subset(icgc_fbi_status, select = c("condensed_id", "Patch et al. Class", 
    "Patch et al. Molecular Subgroup", "BRCA.status", "Subgroup"))

row_annotations <- Reduce(plyr::join, list(cluster_annotations, sig_annotations, 
    subtype_annotations, icgc_fbi_annotations))
row_annotations <- row_annotations %>% column_to_rownames(var = "condensed_id")
row_annotations_noith <- row_annotations

# select_rows <- rownames(subset(row_annotations, `SV-4` < 1))
notx <- subset(specimen_data, str_detect(specimen_type, "Primary") & specimen_donor_treatment_type == 
    "no treatment")$icgc_donor_id
# sample_order <- intersect(sample_order, select_rows)
sample_order <- intersect(sample_order, notx)

# order_fbi <- order(row_annotations[rownames(combined_mat_scaled),]$`SV-8`)

# combined_mat_scaled <- scale(combined_mat)
combined_mat_scaled <- as.data.frame(apply(combined_mat, 2, function(x) (x - 
    median(x, na.rm = TRUE))/mad(x, na.rm = TRUE)))

combined_mat_scaled <- combined_mat_scaled[sample_order, ]
other_mat <- sample_props_mat_scaled[sample_order, ]

pheatmap(clip_values(cbind(combined_mat_scaled, other_mat), 2, -2), cluster_rows = FALSE, 
    cluster_cols = TRUE, annotation_row = row_annotations, fontsize = 6, clustering_distance_cols = "correlation", 
    clustering_method = "ward.D2")

combined_mat_scaled_noith <- combined_mat_scaled
icgc_clinical_labeled <- Reduce(function(x, y) plyr::join(x, y, type = "full"), 
    list(setNames(cluster_annotations, c("icgc_donor_id", "cluster")), icgc_clinical, 
        setNames(subset(icgc_fbi_annotations, select = c("condensed_id", "BRCA.status", 
            "Subgroup")), c("icgc_donor_id", "BRCA.status", "Wang_subgroup"))))

icgc_clinical_labeled$SurvObj <- with(icgc_clinical_labeled, Surv(donor_survival_time, 
    donor_vital_status == "deceased"))

Survival by FBI status …

simple_survival_analysis(SurvObj ~ cluster != 1, data = subset(icgc_clinical_labeled, 
    cluster != 4))

Call:
survival::survdiff(formula = formula, data = data)

                    N Observed Expected (O-E)^2/E (O-E)^2/V
cluster != 1=FALSE 28       26     28.9     0.298     0.627
cluster != 1=TRUE  36       32     29.1     0.297     0.627

 Chisq= 0.6  on 1 degrees of freedom, p= 0.428 
simple_survival_analysis(SurvObj ~ cluster, data = subset(icgc_clinical_labeled, 
    cluster != 4))

Call:
survival::survdiff(formula = formula, data = data)

           N Observed Expected (O-E)^2/E (O-E)^2/V
cluster=1 28       26    28.94     0.298     0.627
cluster=2 25       23    21.32     0.133     0.223
cluster=3 11        9     7.75     0.203     0.249

 Chisq= 0.7  on 2 degrees of freedom, p= 0.716 
simple_survival_analysis(SurvObj ~ Wang_subgroup, data = icgc_clinical_labeled)

Call:
survival::survdiff(formula = formula, data = data)

n=66, 19 observations deleted due to missingness.

                        N Observed Expected (O-E)^2/E (O-E)^2/V
Wang_subgroup=High FBI 36       35     28.6      1.45      2.89
Wang_subgroup=Low FBI  30       25     31.4      1.32      2.89

 Chisq= 2.9  on 1 degrees of freedom, p= 0.0889 

Combined

Sample-level
combined_mat_scaled_all <- rbind.fill(combined_mat_scaled_ith %>% as.data.frame %>% 
    rownames_to_column("condensed_id"), combined_mat_scaled_noith %>% as.data.frame %>% 
    rownames_to_column("condensed_id")) %>% column_to_rownames("condensed_id")

row_annotations_all <- rbind.fill(row_annotations_ith %>% rownames_to_column("condensed_id"), 
    row_annotations_noith %>% rownames_to_column("condensed_id")) %>% column_to_rownames("condensed_id")

sample_order <- sample_props_heat$tree_row$labels[sample_props_heat$tree_row$order]
sample_order <- intersect(sample_order, rownames(combined_mat_scaled_all))

# combined_mat_scaled <- as.data.frame(apply(combined_mat_all, 2,
# function(x) (x-median(x,na.rm=TRUE))/mad(x,na.rm=TRUE)))
combined_mat_scaled <- combined_mat_scaled_all[sample_order, ]

nanostring_vars <- rownames(icgc_celltype_matrix)
combined_mat_scaled_subset <- subset(combined_mat_scaled, select = nanostring_vars)
combined_mat_scaled_subset <- combined_mat_scaled_subset[!apply(combined_mat_scaled_subset, 
    1, function(x) all(is.na(x))), ]
pheatmap(clip_values(combined_mat_scaled_subset, 2, -2), cluster_rows = FALSE, 
    cluster_cols = TRUE, annotation_row = row_annotations_all, fontsize = 6, 
    clustering_distance_cols = "correlation", clustering_method = "ward.D2")

Doesn’t really cluster consistently between ICGC and our samples.

TODO: Batch correct ICGC and our data together (I think I already did this somewhere) – and normalize the final matrix in one step rather than normalizing separately for each subcohort and combining those together. Otherwise we could always be subject to the scenario where the ITH cohort may be skewed in immune response (either all relatively low or high) compared to the ICGC cohort.

TODO: Use absolute counts of mutations for each signature, and cluster based on those. It may be that samples with too low/high mutation counts are not clustering properly.

Patient-level
# ith_icgc_batch_corrected_expression_file <-
# '~/shahlab/projects/ITH_Immune/paper/results/tables/run2/ith_icgc_merged_bc.tsv'

ith_icgc_expression <- fread(ith_icgc_batch_corrected_expression_file)

ith_icgc_celltype_expr <- create_celltype_matrix(ith_icgc_expression, labels, 
    db_path, convert_ids = FALSE)
ith_icgc_pathway_expr <- create_pathway_matrix(ith_icgc_expression, labels, 
    db_path, convert_ids = FALSE)

icgc_specimen_data <- fread(icgc_specimen_file)
summarize_expression_by_patient <- function(expr) {
    primary_specimen_dat <- subset(icgc_specimen_data, str_detect(specimen_type, 
        "Primary"))
    
    notx <- subset(primary_specimen_dat, str_detect(specimen_type, "Primary") & 
        specimen_donor_treatment_type == "no treatment")$icgc_donor_id
    primary_specimen_dat <- subset(primary_specimen_dat, icgc_donor_id %in% 
        notx)
    
    x <- setNames(melt(as.matrix(expr)), c("Name", "sample", "expr"))
    x$patient_id <- map_id(as.character(x$sample), from = "condensed_id", to = "patient_id", 
        db_path)
    idx <- is.na(x$patient_id)
    donor_labels <- df_as_map(primary_specimen_dat, as.character(x$sample[idx]), 
        from = "icgc_specimen_id", to = "icgc_donor_id")
    x$patient_id[idx] <- donor_labels
    x <- subset(x, !is.na(patient_id))
    x_sum <- x %>% group_by(Name, patient_id) %>% summarise(expr = mean(expr, 
        na.rm = TRUE))
    
    res <- dcast(x_sum, formula = Name ~ patient_id, value.var = "expr")
    return(res)
}
NCLUST <- 3
clusters <- cutree(patient_props_heat$tree_row, NCLUST)
patient_clusts <- make_cluster_frame(clusters)

cluster_annotations <- subset(plyr::rename(patient_clusts, c(new_id = "patient_id")), 
    select = c(patient_id, cluster))

ith_icgc_celltype_expr_patient <- summarize_expression_by_patient(ith_icgc_celltype_expr)
ith_icgc_pathway_expr_patient <- summarize_expression_by_patient(ith_icgc_pathway_expr)

expr_dat <- ith_icgc_pathway_expr_patient
combined_patient_mat <- expr_dat %>% as.data.frame %>% column_to_rownames(var = "Name") %>% 
    t %>% as.data.frame %>% rownames_to_column("patient_id")

data_matrices <- list(cluster_annotations, combined_patient_mat)
data_matrices <- lapply(data_matrices, function(x) {
    x
})

combined_df <- Reduce(function(x, y) plyr::join(x, y, type = "inner"), data_matrices)
combined_mat <- subset(combined_df, select = -c(cluster))
rownames(combined_mat) <- NULL
combined_mat <- combined_mat %>% column_to_rownames("patient_id")

SIGS <- c("SV-3", "SNV-5", "SV-6", "SV-8", "SNV-2")
sig_annotations <- rownames_to_column(data.frame(patient_props_mat[, SIGS], 
    check.names = FALSE), var = "patient_id")

subtype_annotations <- subset(icgc_subtypes, select = c("icgc_donor_id", "subtype", 
    "nmf_subtype"))
colnames(subtype_annotations)[1] <- "patient_id"

icgc_fbi_status_patient <- fread(wang_icgc_fbi_status_file)
colnames(icgc_fbi_status_patient) <- mapvalues(colnames(icgc_fbi_status_patient), 
    from = c("Case_ID"), to = c("patient_id"))

icgc_fbi_annotations <- subset(icgc_fbi_status_patient, select = c("patient_id", 
    "Patch et al. Class", "Patch et al. Molecular Subgroup", "BRCA.status", 
    "Subgroup"))

row_annotations <- Reduce(plyr::join, list(cluster_annotations, sig_annotations, 
    subtype_annotations, icgc_fbi_annotations))

row_annotations <- row_annotations %>% column_to_rownames("patient_id")

combined_patient_mat_scaled <- combined_mat %>% scale

patient_order <- patient_props_heat$tree_row$labels[patient_props_heat$tree_row$order]
patient_order <- intersect(patient_order, rownames(combined_patient_mat_scaled))
pheatmap(clip_values(combined_patient_mat_scaled[patient_order, ], 2, -2), cluster_rows = FALSE, 
    cluster_cols = TRUE, annotation_row = row_annotations, fontsize = 6, clustering_distance_cols = "correlation", 
    clustering_method = "ward.D2")

Ancestral-descendant signature proportions

ancdesc_props_snv <- unique(subset(sig_results_snv, select = c("patient_id", 
    "is_ancestral", "chrom", "coord", "ref", "alt", signature_labels[signature_labels %in% 
        colnames(sig_results_snv)]))) %>% summarize_signature_proportions(by = c("patient_id", 
    "is_ancestral"), signature_labels)
ancdesc_props_sv <- unique(subset(sig_results_sv, select = c("patient_id", "prediction_id", 
    "is_ancestral", signature_labels[signature_labels %in% colnames(sig_results_sv)]))) %>% 
    summarize_signature_proportions(by = c("patient_id", "is_ancestral"), signature_labels)

ancdesc_props <- plyr::join(ancdesc_props_snv %>% as.data.frame, ancdesc_props_sv %>% 
    as.data.frame)

ancdesc_props_mat <- subset(ancdesc_props, select = colnames(ancdesc_props)[colnames(ancdesc_props) %in% 
    signature_labels])
ancdesc_props_scaled <- scale(ancdesc_props_mat)
rownames(ancdesc_props_scaled) <- with(ancdesc_props, paste(patient_id, is_ancestral, 
    sep = "_"))

pheatmap(ancdesc_props_scaled, clustering_distance_rows = "euclidean", clustering_method = "ward.D2")

ancdesc_props_melted <- melt(ancdesc_props, id.vars = c("patient_id", "is_ancestral"), 
    measure.vars = intersect(colnames(ancdesc_props), signature_labels), variable.name = "signature", 
    value.name = "proportion")

pvals <- setNames(ddply(ancdesc_props_melted, .(signature), function(x) {
    df <- as.data.frame(x)
    corres <- wilcox.test(proportion ~ is_ancestral, df, paired = TRUE)
    
    pval <- corres$p.value
    eq <- substitute(italic(P) == p, list(p = format(pval, digits = 3)))
    return(as.character(as.expression(eq)))
}), c("signature", "p.value"))

ggplot(ancdesc_props_melted, aes(x = factor(is_ancestral), y = proportion)) + 
    geom_boxplot() + facet_wrap(~signature, scales = "free") + geom_text(data = pvals, 
    aes(x = Inf, y = Inf, label = p.value), hjust = 1.1, vjust = 1.5, size = 3, 
    parse = TRUE) + theme_bw() + theme_Publication()

Origin node signature proportions

Note: We cannot get node-specific rearrangement signatures.

Note: These node labels DO NOT correspond to node labels within the clone phylogeny; they correspond to nodes within the Dollo model.

node_props_snv <- unique(subset(sig_results_snv, select = c("patient_id", "origin_node", 
    "chrom", "coord", "ref", "alt", signature_labels[signature_labels %in% colnames(sig_results_snv)]))) %>% 
    summarize_signature_proportions(by = c("patient_id", "origin_node"), signature_labels, 
        report_count = TRUE)
node_props_mat <- subset(node_props_snv, select = colnames(node_props_snv)[colnames(node_props_snv) %in% 
    signature_labels])
node_props_scaled <- scale(node_props_mat)
rownames(node_props_scaled) <- with(node_props_snv, paste(patient_id, origin_node, 
    sep = "_"))

row_annotations <- subset(node_props_snv, select = c("patient_id", "n"))
row_annotations$n <- log2(row_annotations$n)
row_annotations$patient_id <- factor_id(row_annotations$patient_id, type = "patient_id", 
    db_path)
rownames(row_annotations) <- rownames(node_props_scaled)

clustering_colours <- list(patient_id = pal_patient)

pheatmap(clip_values(node_props_scaled, 2, -2), clustering_distance_rows = "euclidean", 
    clustering_method = "ward.D2", fontsize_row = 5, annotation_row = row_annotations, 
    annotation_colors = clustering_colours)

Looks like there are similar selection pressures acting at each part of the sample phylogeny – i.e. mutation signatures are ‘relatively’ consistent within patients throughout time.

Clonal phylogeny branch-specific signature proportions

NOTE: THE LABELS ON THESE TREES MAY NOT BE THE SAME AS THOSE IN THE MAPSCAPES (ugh).

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)
branch_lengths <- rbind.fill(lapply(tree_branch_data, function(x) x$branch_dat))
snv_cluster <- rbind.fill(lapply(seq_along(snv_cluster_files), function(i) {
    f <- snv_cluster_files[[i]]
    patient_id <- snv_cluster_patients[[i]]
    snv_cluster <- read_snv_cluster(f, clone_branch_length_file, db_path)
    return(snv_cluster)
}))

snv_cluster <- plyr::join(snv_cluster, branch_lengths)
snv_cluster_filtered <- subset(snv_cluster, !is.na(label))

sig_results_snv_cluster <- plyr::join(sig_results_snv, snv_cluster_filtered)
branch_props_snv <- unique(subset(sig_results_snv_cluster, select = c("patient_id", 
    "label", "chrom", "coord", "ref", "alt", signature_labels[signature_labels %in% 
        colnames(sig_results_snv_cluster)]))) %>% summarize_signature_proportions(by = c("patient_id", 
    "label"), signature_labels, report_count = TRUE)
branch_props_snv_melted <- melt(branch_props_snv, id.vars = c("patient_id", 
    "label", "n"), measure.vars = intersect(signature_labels, colnames(branch_props_snv)), 
    variable.name = "signature", value.name = "proportion")
ggplot(branch_props_snv_melted, aes(x = label, y = proportion)) + geom_bar(aes(fill = signature), 
    stat = "identity") + facet_wrap(~patient_id, ncol = 1, scales = "free_x") + 
    theme_bw() + theme_Publication() + geom_text(data = unique(subset(branch_props_snv_melted, 
    select = c("patient_id", "label", "n"))), aes(x = label, y = 1, label = n), 
    vjust = -0.2, stat = "identity") + ylim(c(0, 1.2))

What if we use MAP assignments? (This would allow us to apply chi-square tests.)

TODO: Figure out how to apply tests between k > 2 groups of proportions… in other words, a test of homogeneity.

id_vars <- colnames(sig_results_snv_cluster)[!colnames(sig_results_snv_cluster) %in% 
    signature_labels]

sig_results_snv_cluster_melted <- melt(sig_results_snv_cluster, id.vars = id_vars, 
    measure.vars = intersect(signature_labels, colnames(sig_results_snv_cluster)), 
    variable.name = "signature", value.name = "proportion")
maxes <- sig_results_snv_cluster_melted %>% group_by_(.dots = id_vars) %>% summarise(maxprop = max(proportion))
sig_results_snv_cluster_melted <- plyr::join(sig_results_snv_cluster_melted, 
    maxes)
sig_results_snv_cluster_melted$proportion_map <- ifelse(sig_results_snv_cluster_melted$proportion == 
    sig_results_snv_cluster_melted$maxprop, 1, 0)

sig_results_snv_cluster_casted <- dcast(sig_results_snv_cluster_melted, formula = paste0(paste(id_vars, 
    collapse = "+"), "~ signature"), value.var = "proportion_map")

branch_props_snv_map <- unique(subset(sig_results_snv_cluster_casted, select = c("patient_id", 
    "label", "chrom", "coord", "ref", "alt", signature_labels[signature_labels %in% 
        colnames(sig_results_snv_cluster_casted)]))) %>% summarize_signature_proportions(by = c("patient_id", 
    "label"), signature_labels, report_count = TRUE)

branch_props_snv_map_melted <- melt(branch_props_snv_map, id.vars = c("patient_id", 
    "label", "n"), measure.vars = intersect(signature_labels, colnames(branch_props_snv_map)), 
    variable.name = "signature", value.name = "proportion")
ggplot(branch_props_snv_map_melted, aes(x = label, y = proportion)) + geom_bar(aes(fill = signature), 
    stat = "identity") + facet_wrap(~patient_id, ncol = 1, scales = "free_x") + 
    theme_bw() + theme_Publication() + geom_text(data = unique(subset(branch_props_snv_map_melted, 
    select = c("patient_id", "label", "n"))), aes(x = label, y = 1, label = n), 
    vjust = -0.2, stat = "identity") + ylim(c(0, 1.2))

Adjusted clone trees

trees_age <- trees
age_signature <- "SNV-5"

branch_props_dat <- branch_props_snv_map_melted

tree_objects <- lapply(seq_along(trees_age), function(i) {
    tree <- trees_age[[i]]
    tree_old <- tree
    patient <- names(trees_age)[i]
    branch_props_dat_sub <- subset(branch_props_dat, patient_id == as.numeric(patient) & 
        signature == age_signature)
    branch_props_dat_sub$length <- with(branch_props_dat_sub, proportion * n)
    
    edge_lengths <- tree@edge.length
    idx <- which(edge_lengths == 0)
    to_labels <- tree@label[str_extract(names(edge_lengths), "[0-9]+$")]
    lengths <- df_as_map(branch_props_dat_sub, unname(to_labels), from = "label", 
        to = "length")
    if (length(idx) > 0) {
        lengths[idx] <- 0
    }
    tree@edge.length <- lengths
    names(tree@edge.length) <- names(edge_lengths)
    
    return(list(all = tree_old, age = tree))
})
names(tree_objects) <- names(trees_age)
tmp <- unlist(tree_objects)
# ignore <- lapply(seq_along(tmp), function(i) { print(plot(tmp[[i]],
# show.node.label = TRUE, main = names(tmp)[i])) })
find_ancestors <- function(tree) {
    x <- phylobase:::.phylo4ToDataFrame(tree)
    root <- subset(x, node.type == "root")$label
    root_number <- names(which(tree@label == root))
    direct_descendants <- subset(x, ancestor == as.numeric(root_number))$label
    if (length(direct_descendants) == 1) {
        return(c(root, direct_descendants))
    } else {
        return(root)
    }
}
patients <- unique(branch_props_dat$patient_id)
root_data <- rbind.fill(lapply(patients, function(pat) {
    tree <- trees[[as.character(pat)]]
    ancestors <- find_ancestors(tree)
    rbind.fill(lapply(ancestors, function(x) {
        data.frame(label = x, patient_id = pat)
    }))
}))
root_data$node_type <- "root"
branch_data_annotated <- plyr::join(branch_props_dat, root_data)
branch_data_annotated$node_type[is.na(branch_data_annotated$node_type)] <- "descendant"

root_proportions <- branch_data_annotated %>% subset(node_type == "root") %>% 
    group_by(patient_id, signature) %>% summarise(proportion = weighted.mean(proportion, 
    w = n)) %>% plyr::rename(c(proportion = "root_proportion"))

branch_data_annotated <- plyr::join(branch_data_annotated, root_proportions)
branch_data_annotated$proportion_diff <- with(branch_data_annotated, proportion - 
    root_proportion)
ggplot(branch_data_annotated %>% subset(node_type == "descendant" & n > 40), 
    aes(x = proportion_diff, fill = signature)) + geom_histogram(binwidth = 0.02, 
    alpha = 0.4, position = "identity") + theme_bw() + theme_Publication()

LS0tCnRpdGxlOiAiTXV0YXRpb24gc2lnbmF0dXJlcyIKb3V0cHV0OiAKICBodG1sX25vdGVib29rOgogICAgdG9jOiB0cnVlCiAgICB0b2NfZGVwdGg6IDUKICAgIHRvY19mbG9hdDogdHJ1ZQpwYXJhbXM6CiAgcm1kOiAibXV0YXRpb25fc2lnbmF0dXJlcy5SbWQiCi0tLQogICAgICAgICAgICAgICAgICAgICAgICBgYGB7ciwgZWNobz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KCiMjIyMjIyMjIFNuYWtlbWFrZSBoZWFkZXIgIyMjIyMjIyMKbGlicmFyeShtZXRob2RzKQpTbmFrZW1ha2UgPC0gc2V0Q2xhc3MoCiAgICAiU25ha2VtYWtlIiwKICAgIHNsb3RzID0gYygKICAgICAgICBpbnB1dCA9ICJsaXN0IiwKICAgICAgICBvdXRwdXQgPSAibGlzdCIsCiAgICAgICAgcGFyYW1zID0gImxpc3QiLAogICAgICAgIHdpbGRjYXJkcyA9ICJsaXN0IiwKICAgICAgICB0aHJlYWRzID0gIm51bWVyaWMiLAogICAgICAgIGxvZyA9ICJsaXN0IiwKICAgICAgICByZXNvdXJjZXMgPSAibGlzdCIsCiAgICAgICAgY29uZmlnID0gImxpc3QiLAogICAgICAgIHJ1bGUgPSAiY2hhcmFjdGVyIgogICAgKQopCnNuYWtlbWFrZSA8LSBTbmFrZW1ha2UoCiAgICBpbnB1dCA9IGxpc3QoJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9yZXN1bHRzL3RhYmxlcy9ydW4yL2Nsb25lcy90cmVlX2RhdGEudHN2JywgJy9zaGFobGFiL2FsemhhbmcvZGF0YS9UQ0dBL3N5bmFwc2VfY2xpbkFsbF9kYXRhLnRzdicsICcvc2hhaGxhYi9hbWNwaGVyc29uL3Byb2plY3RzL2l0aDMvaXRoMy9ub3RlYm9va3MvYmVzcG9rZS9pdGhfYnJlYWtwb2ludHMudHN2JywgJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9yZXN1bHRzL21tY3RtX3Jlc3VsdHMvaXRoX2J5LWFuY2VzdHJ5LXNhbXBsZS9vdXRwdXQnLCAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL2RhdGEvZXhwcmVzc2lvbi9uYW5vc3RyaW5nL3BhbmNhbmNlcl9hbm5vdGF0aW9ucy50c3YnLCAnL3NoYWhsYWIvYW1jcGhlcnNvbi9wcm9qZWN0cy9pdGgzL2l0aDMvbm90ZWJvb2tzL2Jlc3Bva2UvaXRoX3NudnMudHN2JywgJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9yZXN1bHRzL21tY3RtX3Jlc3VsdHMvaXRoX2J5LXBhdGllbnRfd2l0aC1vdicsICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy93ZWIvcmVzb3VyY2VzL21tY3RtX2ZpbmFsX3BhdGllbnRfc2lncGxvdC5wbmcnLCAnL3NoYWhsYWIvYWx6aGFuZy9kYXRhL0lDR0MvT1ZBVV9leHByX21lbHRlZC50c3YnLCAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3BhcGVyL3Jlc3VsdHMvd2ViL3Jlc291cmNlcy9tbWN0bV9zYW1wbGVfc2lncGxvdC5wbmcnLCAnL3NoYWhsYWIvYWx6aGFuZy9kYXRhL0lDR0MvaWNnY19wcmltYXJ5X3R1bW91cl9zdWJ0eXBlcy50c3YnLCAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3BhcGVyL3Jlc3VsdHMvd2ViL3Jlc291cmNlcy9tbWN0bV9vdl9jb21iaW5lZF9zaWdwbG90LnBuZycsICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy93ZWIvcmVzb3VyY2VzL21tY3RtX3NhbXBsZV9hZF9zaWdwbG90LnBuZycsICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcmVzdWx0cy9tbWN0bV9yZXN1bHRzL2NvbWJpbmVkX292X21tY3RtL291dHB1dCcsICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvZGF0YS9pdGgvY29tcGxldGUvb2xkX3Byb3BvcnRpb25fc3ViY2xvbmFsLnRzdicsICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy90YWJsZXMvcnVuMi9jbG9uZXMvc252X2NsdXN0ZXIvcGF0aWVudF8xLnRzdicsICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy90YWJsZXMvcnVuMi9jbG9uZXMvc252X2NsdXN0ZXIvcGF0aWVudF8yLnRzdicsICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy90YWJsZXMvcnVuMi9jbG9uZXMvc252X2NsdXN0ZXIvcGF0aWVudF8zLnRzdicsICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy90YWJsZXMvcnVuMi9jbG9uZXMvc252X2NsdXN0ZXIvcGF0aWVudF80LnRzdicsICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy90YWJsZXMvcnVuMi9jbG9uZXMvc252X2NsdXN0ZXIvcGF0aWVudF83LnRzdicsICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy90YWJsZXMvcnVuMi9jbG9uZXMvc252X2NsdXN0ZXIvcGF0aWVudF85LnRzdicsICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy90YWJsZXMvcnVuMi9jbG9uZXMvc252X2NsdXN0ZXIvcGF0aWVudF8xMC50c3YnLCAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3BhcGVyL3Jlc3VsdHMvdGFibGVzL3J1bjIvY2xvbmVzL3Nudl9jbHVzdGVyL3BhdGllbnRfMTEudHN2JywgJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9yZXN1bHRzL3RhYmxlcy9ydW4yL2Nsb25lcy9zbnZfY2x1c3Rlci9wYXRpZW50XzEyLnRzdicsICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy90YWJsZXMvcnVuMi9jbG9uZXMvc252X2NsdXN0ZXIvcGF0aWVudF8xMy50c3YnLCAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3BhcGVyL3Jlc3VsdHMvdGFibGVzL3J1bjIvY2xvbmVzL3Nudl9jbHVzdGVyL3BhdGllbnRfMTQudHN2JywgJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9yZXN1bHRzL3RhYmxlcy9ydW4yL2Nsb25lcy9zbnZfY2x1c3Rlci9wYXRpZW50XzE1LnRzdicsICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy90YWJsZXMvcnVuMi9jbG9uZXMvc252X2NsdXN0ZXIvcGF0aWVudF8xNi50c3YnLCAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3BhcGVyL3Jlc3VsdHMvdGFibGVzL3J1bjIvY2xvbmVzL3Nudl9jbHVzdGVyL3BhdGllbnRfMTcudHN2JywgJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9yZXN1bHRzL3RhYmxlcy9ydW4yL2l0aF9pY2djX21lcmdlZF9iYy50c3YnLCAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3BhcGVyL3Jlc3VsdHMvdGFibGVzL3J1bjIvbW9sc3VidHlwZXMudHN2JywgJy9zaGFobGFiL2FsemhhbmcvcGlwZWxpbmVfb3V0cHV0cy9pdGhfaW1tdW5lL21peGNyL21peGNyX3J1bnMvaXRoXzFfMl8zL21peGNyNS9wb3N0cHJvY2Vzcy9UUkIvcG9zdGZpbHRlcl9kaXZlcnNpdHlfc3RhdHMvZGl2ZXJzaXR5LnN0cmljdC5yZXNhbXBsZWQudHh0JywgJ1JtZC9tdXRhdGlvbl9zaWduYXR1cmVzLlJtZCcsICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy90YWJsZXMvcnVuMi9paGNfdGFibGVfc2xpZGUudHN2JywgJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9yZXN1bHRzL3RhYmxlcy9ydW4yL2Nsb25lcy9jbG9uZV9kYXRhLnRzdicsICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcmVzdWx0cy9uYW5vc3RyaW5nX3Jlc3VsdHMvaXRoX2Z1bGwvcWMvbGltbWFfcXVhbnRpbGUvbm9ybWFsaXplZF9leHByZXNzaW9uX3ZvYV9sYWJlbHNfZmlsdGVyZWQudHN2JywgJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9yZXN1bHRzL21tY3RtX3Jlc3VsdHMvaXRoX2J5LXNhbXBsZS9vdXRwdXQnLCAnL3NoYWhsYWIvYWx6aGFuZy9kYXRhL0lDR0Mvc3BlY2ltZW4udHN2JywgJy9zaGFobGFiL2FsemhhbmcvcGlwZWxpbmVfb3V0cHV0cy9pdGhfaW1tdW5lL21peGNyL21peGNyX3J1bnMvaXRoXzFfMl8zL21peGNyNS9wb3N0cHJvY2Vzcy9JR0gvcG9zdGZpbHRlcl9kaXZlcnNpdHlfc3RhdHMvZGl2ZXJzaXR5LnN0cmljdC5yZXNhbXBsZWQudHh0JywgJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9hbmFseXNpcy9SbWQvX3NpdGUueW1sJywgJy9zaGFobGFiL2FsemhhbmcvZGF0YS9JQ0dDL09WQVVfZXhwcl9tYXRyaXgudHN2JywgJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9yZXN1bHRzL3dlYi9yZXNvdXJjZXMvbW1jdG1fcGF0aWVudF9hZF9zaWdwbG90LnBuZycsICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy90YWJsZXMvcnVuMi9jbG9uZXMvYnJhbmNoX2RhdGEudHN2JywgJy9zaGFobGFiL2FsemhhbmcvZGF0YS9JQ0dDL25nLjM4NDktUzEyLnR4dCcsICcvc2hhaGxhYi9hbHpoYW5nL2RhdGEvVENHQS90Y2dhX292X2Fubm90YXRpb25fc3VwMTMudHh0JywgJy9zaGFobGFiL2FsemhhbmcvZGF0YS9JQ0dDL2Rvbm9yLk9WLUFVLnRzdicsICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy90YWJsZXMvcnVuMi9paGNfdGFibGUudHN2JywgJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9yZXN1bHRzL21tY3RtX3Jlc3VsdHMvaXRoX2J5LXBhdGllbnQtYW5jZXN0cnkvb3V0cHV0JywgJy9zaGFobGFiL2FsemhhbmcvZGF0YS9UQ0dBL2V4cHJfbWF0cml4X25vcm1hbGl6ZV9zdGFuZGFyZGl6ZV9ub2R1cGxpY2F0ZXMudHN2JywgInRjZ2FfY2xpbmljYWwiID0gJy9zaGFobGFiL2FsemhhbmcvZGF0YS9UQ0dBL3N5bmFwc2VfY2xpbkFsbF9kYXRhLnRzdicsICJtYXN0ZXJfYnJlYWtwb2ludF9maWxlIiA9ICcvc2hhaGxhYi9hbWNwaGVyc29uL3Byb2plY3RzL2l0aDMvaXRoMy9ub3RlYm9va3MvYmVzcG9rZS9pdGhfYnJlYWtwb2ludHMudHN2JywgIm1tY3RtX3BhdGllbnRfYWRfZGlyIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcmVzdWx0cy9tbWN0bV9yZXN1bHRzL2l0aF9ieS1wYXRpZW50LWFuY2VzdHJ5L291dHB1dCcsICJtbWN0bV9zYW1wbGVfYWRfZGlyIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcmVzdWx0cy9tbWN0bV9yZXN1bHRzL2l0aF9ieS1hbmNlc3RyeS1zYW1wbGUvb3V0cHV0JywgIm5hbm9zdHJpbmdfYW5ub3RhdGlvbnMiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9kYXRhL2V4cHJlc3Npb24vbmFub3N0cmluZy9wYW5jYW5jZXJfYW5ub3RhdGlvbnMudHN2JywgIm1hc3Rlcl92YXJpYW50X2ZpbGUiID0gJy9zaGFobGFiL2FtY3BoZXJzb24vcHJvamVjdHMvaXRoMy9pdGgzL25vdGVib29rcy9iZXNwb2tlL2l0aF9zbnZzLnRzdicsICJpY2djX2V4cHJfbWVsdGVkIiA9ICcvc2hhaGxhYi9hbHpoYW5nL2RhdGEvSUNHQy9PVkFVX2V4cHJfbWVsdGVkLnRzdicsICJtbWN0bV9maW5hbF9wYXRpZW50X3NpZ3Bsb3QiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9yZXN1bHRzL3dlYi9yZXNvdXJjZXMvbW1jdG1fZmluYWxfcGF0aWVudF9zaWdwbG90LnBuZycsICJjbG9uZV90cmVlX2ZpbGUiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9yZXN1bHRzL3RhYmxlcy9ydW4yL2Nsb25lcy90cmVlX2RhdGEudHN2JywgIm1tY3RtX3NhbXBsZV9zaWdwbG90IiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy93ZWIvcmVzb3VyY2VzL21tY3RtX3NhbXBsZV9zaWdwbG90LnBuZycsICJpY2djX3N1YnR5cGVzIiA9ICcvc2hhaGxhYi9hbHpoYW5nL2RhdGEvSUNHQy9pY2djX3ByaW1hcnlfdHVtb3VyX3N1YnR5cGVzLnRzdicsICJtbWN0bV9vdl9jb21iaW5lZF9zaWdwbG90IiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy93ZWIvcmVzb3VyY2VzL21tY3RtX292X2NvbWJpbmVkX3NpZ3Bsb3QucG5nJywgIm1tY3RtX3NhbXBsZV9hZF9zaWdwbG90IiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy93ZWIvcmVzb3VyY2VzL21tY3RtX3NhbXBsZV9hZF9zaWdwbG90LnBuZycsICJtbWN0bV9vdl9jb21iaW5lZF9kaXIiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9yZXN1bHRzL21tY3RtX3Jlc3VsdHMvY29tYmluZWRfb3ZfbW1jdG0vb3V0cHV0JywgInN1YmNsb25hbGl0eSIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL2RhdGEvaXRoL2NvbXBsZXRlL29sZF9wcm9wb3J0aW9uX3N1YmNsb25hbC50c3YnLCAic252X2NsdXN0ZXJfZmlsZXMiID0gYygnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3BhcGVyL3Jlc3VsdHMvdGFibGVzL3J1bjIvY2xvbmVzL3Nudl9jbHVzdGVyL3BhdGllbnRfMS50c3YnLCAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3BhcGVyL3Jlc3VsdHMvdGFibGVzL3J1bjIvY2xvbmVzL3Nudl9jbHVzdGVyL3BhdGllbnRfMi50c3YnLCAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3BhcGVyL3Jlc3VsdHMvdGFibGVzL3J1bjIvY2xvbmVzL3Nudl9jbHVzdGVyL3BhdGllbnRfMy50c3YnLCAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3BhcGVyL3Jlc3VsdHMvdGFibGVzL3J1bjIvY2xvbmVzL3Nudl9jbHVzdGVyL3BhdGllbnRfNC50c3YnLCAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3BhcGVyL3Jlc3VsdHMvdGFibGVzL3J1bjIvY2xvbmVzL3Nudl9jbHVzdGVyL3BhdGllbnRfNy50c3YnLCAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3BhcGVyL3Jlc3VsdHMvdGFibGVzL3J1bjIvY2xvbmVzL3Nudl9jbHVzdGVyL3BhdGllbnRfOS50c3YnLCAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3BhcGVyL3Jlc3VsdHMvdGFibGVzL3J1bjIvY2xvbmVzL3Nudl9jbHVzdGVyL3BhdGllbnRfMTAudHN2JywgJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9yZXN1bHRzL3RhYmxlcy9ydW4yL2Nsb25lcy9zbnZfY2x1c3Rlci9wYXRpZW50XzExLnRzdicsICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy90YWJsZXMvcnVuMi9jbG9uZXMvc252X2NsdXN0ZXIvcGF0aWVudF8xMi50c3YnLCAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3BhcGVyL3Jlc3VsdHMvdGFibGVzL3J1bjIvY2xvbmVzL3Nudl9jbHVzdGVyL3BhdGllbnRfMTMudHN2JywgJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9yZXN1bHRzL3RhYmxlcy9ydW4yL2Nsb25lcy9zbnZfY2x1c3Rlci9wYXRpZW50XzE0LnRzdicsICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy90YWJsZXMvcnVuMi9jbG9uZXMvc252X2NsdXN0ZXIvcGF0aWVudF8xNS50c3YnLCAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3BhcGVyL3Jlc3VsdHMvdGFibGVzL3J1bjIvY2xvbmVzL3Nudl9jbHVzdGVyL3BhdGllbnRfMTYudHN2JywgJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9yZXN1bHRzL3RhYmxlcy9ydW4yL2Nsb25lcy9zbnZfY2x1c3Rlci9wYXRpZW50XzE3LnRzdicpLCAiaXRoX2ljZ2NfYmMiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9yZXN1bHRzL3RhYmxlcy9ydW4yL2l0aF9pY2djX21lcmdlZF9iYy50c3YnLCAibW9sZWN1bGFyX3N1YnR5cGVzIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy90YWJsZXMvcnVuMi9tb2xzdWJ0eXBlcy50c3YnLCAidGNyX2RpdmVyc2l0eSIgPSAnL3NoYWhsYWIvYWx6aGFuZy9waXBlbGluZV9vdXRwdXRzL2l0aF9pbW11bmUvbWl4Y3IvbWl4Y3JfcnVucy9pdGhfMV8yXzMvbWl4Y3I1L3Bvc3Rwcm9jZXNzL1RSQi9wb3N0ZmlsdGVyX2RpdmVyc2l0eV9zdGF0cy9kaXZlcnNpdHkuc3RyaWN0LnJlc2FtcGxlZC50eHQnLCAibm90ZWJvb2siID0gJ1JtZC9tdXRhdGlvbl9zaWduYXR1cmVzLlJtZCcsICJpaGNfdGFibGVfc2xpZGUiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9yZXN1bHRzL3RhYmxlcy9ydW4yL2loY190YWJsZV9zbGlkZS50c3YnLCAiY2xvbmVfcHJldmFsZW5jZV9maWxlIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy90YWJsZXMvcnVuMi9jbG9uZXMvY2xvbmVfZGF0YS50c3YnLCAibmFub3N0cmluZ19kYXRhIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcmVzdWx0cy9uYW5vc3RyaW5nX3Jlc3VsdHMvaXRoX2Z1bGwvcWMvbGltbWFfcXVhbnRpbGUvbm9ybWFsaXplZF9leHByZXNzaW9uX3ZvYV9sYWJlbHNfZmlsdGVyZWQudHN2JywgIm1tY3RtX3NhbXBsZV9kaXIiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9yZXN1bHRzL21tY3RtX3Jlc3VsdHMvaXRoX2J5LXNhbXBsZS9vdXRwdXQnLCAiaWNnY19zcGVjaW1lbiIgPSAnL3NoYWhsYWIvYWx6aGFuZy9kYXRhL0lDR0Mvc3BlY2ltZW4udHN2JywgImJjcl9kaXZlcnNpdHkiID0gJy9zaGFobGFiL2FsemhhbmcvcGlwZWxpbmVfb3V0cHV0cy9pdGhfaW1tdW5lL21peGNyL21peGNyX3J1bnMvaXRoXzFfMl8zL21peGNyNS9wb3N0cHJvY2Vzcy9JR0gvcG9zdGZpbHRlcl9kaXZlcnNpdHlfc3RhdHMvZGl2ZXJzaXR5LnN0cmljdC5yZXNhbXBsZWQudHh0JywgInNpdGVfZmlsZSIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3BhcGVyL2FuYWx5c2lzL1JtZC9fc2l0ZS55bWwnLCAiaWNnY19leHByX21hdCIgPSAnL3NoYWhsYWIvYWx6aGFuZy9kYXRhL0lDR0MvT1ZBVV9leHByX21hdHJpeC50c3YnLCAibW1jdG1fcGF0aWVudF9hZF9zaWdwbG90IiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy93ZWIvcmVzb3VyY2VzL21tY3RtX3BhdGllbnRfYWRfc2lncGxvdC5wbmcnLCAiY2xvbmVfYnJhbmNoX2xlbmd0aF9maWxlIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy90YWJsZXMvcnVuMi9jbG9uZXMvYnJhbmNoX2RhdGEudHN2JywgIndhbmdfZmJpX3N0YXR1cyIgPSAnL3NoYWhsYWIvYWx6aGFuZy9kYXRhL0lDR0MvbmcuMzg0OS1TMTIudHh0JywgInRjZ2Ffb3ZfYW5ub3RhdGlvbiIgPSAnL3NoYWhsYWIvYWx6aGFuZy9kYXRhL1RDR0EvdGNnYV9vdl9hbm5vdGF0aW9uX3N1cDEzLnR4dCcsICJpY2djX2NsaW5pY2FsIiA9ICcvc2hhaGxhYi9hbHpoYW5nL2RhdGEvSUNHQy9kb25vci5PVi1BVS50c3YnLCAiaWhjX3RhYmxlIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy90YWJsZXMvcnVuMi9paGNfdGFibGUudHN2JywgIm1tY3RtX2ZpbmFsX3BhdGllbnRfZGlyIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcmVzdWx0cy9tbWN0bV9yZXN1bHRzL2l0aF9ieS1wYXRpZW50X3dpdGgtb3YnLCAidGNnYV9leHByX21hdCIgPSAnL3NoYWhsYWIvYWx6aGFuZy9kYXRhL1RDR0EvZXhwcl9tYXRyaXhfbm9ybWFsaXplX3N0YW5kYXJkaXplX25vZHVwbGljYXRlcy50c3YnKSwKICAgIG91dHB1dCA9IGxpc3QoJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9yZXN1bHRzL3dlYi9tdXRhdGlvbl9zaWduYXR1cmVzLm5iLmh0bWwnKSwKICAgIHBhcmFtcyA9IGxpc3QoYygnRV9DRDhfZGVuc2l0eScsICdFX0NENF9kZW5zaXR5JywgJ0VfQ0QyMF9kZW5zaXR5JywgJ0VfUGxhc21hX2RlbnNpdHknLCAnU19DRDhfZGVuc2l0eScsICdTX0NENF9kZW5zaXR5JywgJ1NfQ0QyMF9kZW5zaXR5JywgJ1NfUGxhc21hX2RlbnNpdHknLCAnVF9DRDhfZGVuc2l0eScsICdUX0NENF9kZW5zaXR5JywgJ1RfQ0QyMF9kZW5zaXR5JywgJ1RfUGxhc21hX2RlbnNpdHknKSwgJ2l0aGktYW5hbHlzaXMtbXV0YXRpb24tc2lnbmF0dXJlLW5vdGVib29rJywgJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9tZXRhZGF0YS9kYi9pbW11bmVfcHJvamVjdC5zcWxpdGUzJywgYygnMScsICcyJywgJzMnLCAnNCcsICc3JywgJzknLCAnMTAnLCAnMTEnLCAnMTInLCAnMTMnLCAnMTQnLCAnMTUnLCAnMTYnLCAnMTcnKSwgIm11dHNpZ190aWx0eXBlcyIgPSBjKCdFX0NEOF9kZW5zaXR5JywgJ0VfQ0Q0X2RlbnNpdHknLCAnRV9DRDIwX2RlbnNpdHknLCAnRV9QbGFzbWFfZGVuc2l0eScsICdTX0NEOF9kZW5zaXR5JywgJ1NfQ0Q0X2RlbnNpdHknLCAnU19DRDIwX2RlbnNpdHknLCAnU19QbGFzbWFfZGVuc2l0eScsICdUX0NEOF9kZW5zaXR5JywgJ1RfQ0Q0X2RlbnNpdHknLCAnVF9DRDIwX2RlbnNpdHknLCAnVF9QbGFzbWFfZGVuc2l0eScpLCAibmFtZSIgPSAnaXRoaS1hbmFseXNpcy1tdXRhdGlvbi1zaWduYXR1cmUtbm90ZWJvb2snLCAiZGIiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9tZXRhZGF0YS9kYi9pbW11bmVfcHJvamVjdC5zcWxpdGUzJywgInNudl9jbHVzdGVyX3BhdGllbnRzIiA9IGMoJzEnLCAnMicsICczJywgJzQnLCAnNycsICc5JywgJzEwJywgJzExJywgJzEyJywgJzEzJywgJzE0JywgJzE1JywgJzE2JywgJzE3JykpLAogICAgd2lsZGNhcmRzID0gbGlzdCgpLAogICAgdGhyZWFkcyA9IDEsCiAgICBsb2cgPSBsaXN0KCcvc2hhaGxhYi9hbHpoYW5nL2NsdXN0dG1wL3BhcGVyYW5hbHlzaXMyL211dGF0aW9uX3NpZ25hdHVyZXMubG9nJyksCiAgICByZXNvdXJjZXMgPSBsaXN0KCksCiAgICBjb25maWcgPSBsaXN0KCJub3RlYm9va19kaXIiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9yZXN1bHRzL3dlYicsICJuY2x1c3RzIiA9IDMsICJ0YWJsZV9kaXIiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9yZXN1bHRzL3RhYmxlcy9ydW4yJywgIm1tY3RtX292X2NvbWJpbmVkX3NpZ3Bsb3QiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9yZXN1bHRzL21tY3RtX3Jlc3VsdHMvY29tYmluZWRfb3ZfbW1jdG0vcGxvdHMvb3Zfc252LXN2X3NpZ3NfbXVsdGlwYW5lbC5wZGYnLCAibXZjbHVzdF9uY2x1c3QiID0gMywgIm11dGF0aW9uX3NpZ25hdHVyZV9ub3RlYm9vayIgPSAnUm1kL211dGF0aW9uX3NpZ25hdHVyZXMuUm1kJywgImV4YW1wbGVfbXNhX3Bsb3QiID0gJy9zaGFobGFiL2FsemhhbmcvcGlwZWxpbmVfb3V0cHV0cy9pdGhfaW1tdW5lL2lncGFydGl0aW9uL3J1bjEzL29sZC9hbGlnbm1lbnRfcGxvdHMvbXNhL2l0aDJfMi9jbHVzdDkvaW5kZWxfcmV2ZXJzZWQuaHRtbCcsICJkZWZhdWx0X3NhbXBsZXIiID0gJ0hNQycsICJtbWN0bV9wYXRpZW50X2FuY2VzdHJhbF9kZXNjZW5kYW50X3Jlc3VsdF9kaXIiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9yZXN1bHRzL21tY3RtX3Jlc3VsdHMvaXRoX2J5LXBhdGllbnQtYW5jZXN0cnkvb3V0cHV0JywgImJjcl9jbG9ub3R5cGVzIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3BpcGVsaW5lX291dHB1dHMvaXRoX2ltbXVuZS9taXhjci9taXhjcl9ydW5zL2l0aF8xXzJfMy9taXhjcjUvY2xvbm90eXBlcy9JR0hfY2xvbm90eXBlc19maWx0ZXJlZC50eHQnLCAibWFzdGVyX3ZhcmlhbnRfZmlsZSIgPSAnL3NoYWhsYWIvYW1jcGhlcnNvbi9wcm9qZWN0cy9pdGgzL2l0aDMvbm90ZWJvb2tzL2Jlc3Bva2UvaXRoX3NudnMudHN2JywgImljZ2NfZXhwcl9tZWx0ZWQiID0gJy9zaGFobGFiL2FsemhhbmcvZGF0YS9JQ0dDL09WQVVfZXhwcl9tZWx0ZWQudHN2JywgInNwYXRpYWxfbm90ZWJvb2siID0gJ1JtZC9zcGF0aWFsX2FuYWx5c2lzLlJtZCcsICJ0Y2dhX2NsaW5pY2FsIiA9ICcvc2hhaGxhYi9hbHpoYW5nL2RhdGEvVENHQS9zeW5hcHNlX2NsaW5BbGxfZGF0YS50c3YnLCAiaXRoX3N0YXRfdHlwZXMiID0gYygnZW50cm9weScsICdwb3N0cHJvY2Vzc2VkX2RpdmVyZ2VuY2UnLCAnY29tYmluZWRfaXRoX25vcm1hbGl6ZWQnLCAncHJvcG9ydGlvbl9zdWJjbG9uYWwnKSwgInRpbHNfZm9yX2NsdXN0ZXIiID0gYygnRV9DRDhfZGVuc2l0eScsICdFX0NENF9kZW5zaXR5JywgJ0VfQ0QyMF9kZW5zaXR5JywgJ0VfUGxhc21hX2RlbnNpdHknLCAnU19DRDhfZGVuc2l0eScsICdTX0NENF9kZW5zaXR5JywgJ1NfQ0QyMF9kZW5zaXR5JywgJ1NfUGxhc21hX2RlbnNpdHknKSwgIml0aF9zdGF0aXN0aWNzX25vdGVib29rIiA9ICdSbWQvaXRoX3N0YXRpc3RpY3MuUm1kJywgInByZXZhbGVuY2VfdGhyZXNob2xkIiA9IDAuMDEsICJ0aWxfY2xhc3NpZmllcl9ub3RlYm9vayIgPSAnUm1kL3RpbF9jbGFzc2lmaWVyLlJtZCcsICJleGFtcGxlX2Fubm90YXRpb25zIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3BpcGVsaW5lX291dHB1dHMvaXRoX2ltbXVuZS9pZ3BhcnRpdGlvbi9ydW4xMy9maW5hbF9wYXJ0aXRpb25zL2l0aDJfMi9jbHVzdDkvYW5ub3RhdGlvbnNfZmxhZ2dlZC50c3YnLCAidmFyaWFiaWxpdHlfdHlwZSIgPSAnc3RhYmlsaXplJywgImltbXVuZV92YXJpYWJpbGl0eV9ub3RlYm9vayIgPSAnUm1kL2ltbXVuZV92YXJpYWJpbGl0eS5SbWQnLCAiaW1tdHlwZXJfbW9kZWxzIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcmVzdWx0cy9pbW10eXBlcl9yZXN1bHRzL2tsYXJlbmJlZWsvYWFfdmovZ3JhZGJvb3N0JywgImJlbmNobWFya2RpciIgPSAnL3NoYWhsYWIvYWx6aGFuZy9iZW5jaG1hcmtzL3BhcGVyYW5hbHlzaXMyJywgIm5hbm9zdHJpbmdfZGF0YSIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3Jlc3VsdHMvbmFub3N0cmluZ19yZXN1bHRzL2l0aF9mdWxsL3FjL2xpbW1hX3F1YW50aWxlL25vcm1hbGl6ZWRfZXhwcmVzc2lvbl92b2FfbGFiZWxzX2ZpbHRlcmVkLnRzdicsICJ4Y3JfbWFwcGluZ19ub3RlYm9vayIgPSAnUm1kL3hjcl9tYXBwaW5nLlJtZCcsICJpaGNfcnVuMSIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL2RhdGEvaWhjL2NkOGNkM2NkMjAvdmFsaWRhdGVkX3N0YXRzX3dlaWdodGVkX25ldy5yZGF0YScsICJwaGVub3R5cGVfdGhyZXNob2xkIiA9IDAuODUsICJuZW9lZGl0aW5nX291dGRpciIgPSAnL3NoYWhsYWIvYWx6aGFuZy9waXBlbGluZV9vdXRwdXRzL2l0aF9pbW11bmUvbmVvZWRpdGluZy9ydW40JywgInRjZ2Ffb3ZfYW5ub3RhdGlvbnMiID0gJy9zaGFobGFiL2FsemhhbmcvZGF0YS9UQ0dBL3RjZ2Ffb3ZfYW5ub3RhdGlvbl9zdXAxMy50eHQnLCAibG9nZGlyIiA9ICcvc2hhaGxhYi9hbHpoYW5nL2NsdXN0dG1wL3BhcGVyYW5hbHlzaXMyJywgInhjcl9kaXN0YW5jZV9tZXRob2QiID0gJ2hvcm4nLCAiZGIiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9tZXRhZGF0YS9kYi9pbW11bmVfcHJvamVjdC5zcWxpdGUzJywgImljZ2NfbW9sZWN1bGFyX3N1YnR5cGVzIiA9ICcvc2hhaGxhYi9hbHpoYW5nL2RhdGEvSUNHQy9pY2djX3ByaW1hcnlfdHVtb3VyX3N1YnR5cGVzLnRzdicsICJuYW5vc3RyaW5nX3NpZ25hdHVyZV9ub3RlYm9vayIgPSAnUm1kL25hbm9zdHJpbmdfc2lnbmF0dXJlcy5SbWQnLCAiaW50ZXJtZWRpYXRlX2RpciIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3BhcGVyL3Jlc3VsdHMvaW50ZXJtZWRpYXRlcy9ydW4yJywgIlBOR19ERU5TSVRZIiA9IDMwMCwgImJjcnBoeWxvX2NvcnJlbGF0aW9uc19ub3RlYm9vayIgPSAnUm1kL2Jjcl9waHlsb19jb3JyZWxhdGlvbnMuUm1kJywgIm1tY3RtX2ZpbmFsX3BhdGllbnRfZGlyIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcmVzdWx0cy9tbWN0bV9yZXN1bHRzL2l0aF9ieS1wYXRpZW50X3dpdGgtb3YnLCAibW1jdG1fc2FtcGxlX3NpZ3Bsb3QiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9yZXN1bHRzL21tY3RtX3Jlc3VsdHMvaXRoX2J5LXNhbXBsZS9wbG90cy9pdGgtYnktc2FtcGxlX3Nudi1zdl9zaWdzX211bHRpcGFuZWwucGRmJywgIml0aF90aWxfbm90ZWJvb2siID0gJ1JtZC9pdGhfdGlsX2RlbnNpdGllcy5SbWQnLCAiaW1tdHlwZXJfbGVuZ3RocyIgPSAnMTEgMTIgMTMgMTQgMTUgMTYgMTcgMTgnLCAieGNyX2Nsb25lc19ub3RlYm9vayIgPSAnUm1kL3hjcl9jbG9uZXNfYW5hbHlzaXMuUm1kJywgIm5lb2FudGlnZW5fZWRpdGluZ19ub3RlYm9vayIgPSAnUm1kL2ltbXVub2VkaXRpbmcuUm1kJywgImxpYnJhcnlfc2l6ZXMiID0gJy9zaGFobGFiL2FsemhhbmcvcGlwZWxpbmVfb3V0cHV0cy9pdGhfaW1tdW5lL21peGNyL21peGNyX3J1bnMvaXRoXzFfMl8zL21peGNyNS9saWJyYXJ5X3NpemVzLnRzdicsICJpZ3BhcnRpdGlvbl9vdXRkaXIiID0gJy9zaGFobGFiL2FsemhhbmcvcGlwZWxpbmVfb3V0cHV0cy9pdGhfaW1tdW5lL2lncGFydGl0aW9uL3J1bjIyJywgImRyaXZlcl9hbmFseXNpc19ub3RlYm9vayIgPSAnUm1kL2RyaXZlcl9hbmFseXNpcy5SbWQnLCAidGNyX2RpdmVyc2l0eSIgPSAnL3NoYWhsYWIvYWx6aGFuZy9waXBlbGluZV9vdXRwdXRzL2l0aF9pbW11bmUvbWl4Y3IvbWl4Y3JfcnVucy9pdGhfMV8yXzMvbWl4Y3I1L3Bvc3Rwcm9jZXNzL1RSQi9wb3N0ZmlsdGVyX2RpdmVyc2l0eV9zdGF0cy9kaXZlcnNpdHkuc3RyaWN0LnJlc2FtcGxlZC50eHQnLCAidl9kaWN0aW9uYXJ5IiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvc3VicHJvamVjdHMvaW1tdHlwZXIvbWV0YWRhdGEvaW1ndC9Ib21vX3NhcGllbnNfVFJCVi5mYXN0YScsICJjbG9uZV9wcmV2YWxlbmNlX2ZpbGUiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9kYXRhL2l0aC9jb21wbGV0ZS9jbG9uZV9kYXRhLnRzdicsICJpaGNfeGNyX3N0YXRzX25vdGVib29rIiA9ICdSbWQvaWhjX3hjcl9zdGF0cy5SbWQnLCAibW1jdG1fc2FtcGxlX3Jlc3VsdF9kaXIiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9yZXN1bHRzL21tY3RtX3Jlc3VsdHMvaXRoX2J5LXNhbXBsZS9vdXRwdXQnLCAibW1jdG1fYW5jZXN0cmFsX2Rlc2NlbmRhbnRfcmVzdWx0X2RpciIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3Jlc3VsdHMvbW1jdG1fcmVzdWx0cy9pdGhfYnktYW5jZXN0cnktc2FtcGxlL291dHB1dCcsICJtdWx0aXZpZXdjbHVzdGVyaW5nX25vdGVib29rIiA9ICdSbWQvbXVsdGl2aWV3Y2x1c3RlcmluZy5SbWQnLCAiY2xvbmFsX3NhbXBsZXJzIiA9IGMoJ0hNQycsICdOVVRTJyksICJpdGhfcHJvamVjdF9yZXN1bHRzIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL2l0aDMvZGF0YS9yZXN1bHRzJywgIm1hc3Rlcl9icmVha3BvaW50X2ZpbGUiID0gJy9zaGFobGFiL2FtY3BoZXJzb24vcHJvamVjdHMvaXRoMy9pdGgzL25vdGVib29rcy9iZXNwb2tlL2l0aF9icmVha3BvaW50cy50c3YnLCAiYmNycGh5bG9fdGlsdHlwZXMiID0gYygnRV9DRDhfZGVuc2l0eScsICdFX0NENF9kZW5zaXR5JywgJ0VfQ0QyMF9kZW5zaXR5JywgJ0VfUGxhc21hX2RlbnNpdHknLCAnU19DRDhfZGVuc2l0eScsICdTX0NENF9kZW5zaXR5JywgJ1NfQ0QyMF9kZW5zaXR5JywgJ1NfUGxhc21hX2RlbnNpdHknLCAnVF9DRDhfZGVuc2l0eScsICdUX0NENF9kZW5zaXR5JywgJ1RfQ0QyMF9kZW5zaXR5JywgJ1RfUGxhc21hX2RlbnNpdHknKSwgImJjcl9kaXZlcnNpdHkiID0gJy9zaGFobGFiL2FsemhhbmcvcGlwZWxpbmVfb3V0cHV0cy9pdGhfaW1tdW5lL21peGNyL21peGNyX3J1bnMvaXRoXzFfMl8zL21peGNyNS9wb3N0cHJvY2Vzcy9JR0gvcG9zdGZpbHRlcl9kaXZlcnNpdHlfc3RhdHMvZGl2ZXJzaXR5LnN0cmljdC5yZXNhbXBsZWQudHh0JywgIlBOR19RVUFMSVRZIiA9IDMwMCwgImNsb25lX3RyZWVfZmlsZSIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL2RhdGEvaXRoL2NvbXBsZXRlL3RyZWVfZGF0YS50c3YnLCAibW1jdG1fZmluYWxfcGF0aWVudF9zaWdwbG90IiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcmVzdWx0cy9tbWN0bV9yZXN1bHRzL2l0aF9ieS1wYXRpZW50X3dpdGgtb3YvcGxvdHMvaXRoLWJ5LXBhdGllbnRfc252LXN2X3NpZ3NfbXVsdGlwYW5lbC5wZGYnLCAic2l0ZV9maWxlIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvYW5hbHlzaXMvUm1kL19zaXRlLnltbCcsICJwYXRpZW50c19mb3JfY2xvbmFsIiA9IGMoMSwgMiwgMywgNCwgNywgOSwgMTAsIDExLCAxMiwgMTMsIDE0LCAxNSwgMTYsIDE3KSwgIm1tY3RtX3NhbXBsZV9hZF9zaWdwbG90IiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcmVzdWx0cy9tbWN0bV9yZXN1bHRzL2l0aF9ieS1hbmNlc3RyeS1zYW1wbGUvcGxvdHMvaXRoLWJ5LWFuY2VzdHJhbC1zYW1wbGVfc252LXN2X3NpZ3NfbXVsdGlwYW5lbC5wZGYnLCAic2FkX25vdGVib29rIiA9ICdSbWQvc3BlY2llc19hYnVuZGFuY2VfZGlzdHJpYnV0aW9ucy5SbWQnLCAieGNyX3FjX25vdGVib29rIiA9ICdSbWQvcmVwbGljYXRlcy5SbWQnLCAibXZjbHVzdF90aWx0eXBlcyIgPSBjKCdFX0NEOF9kZW5zaXR5JywgJ0VfQ0Q0X2RlbnNpdHknLCAnRV9DRDIwX2RlbnNpdHknLCAnRV9QbGFzbWFfZGVuc2l0eScsICdTX0NEOF9kZW5zaXR5JywgJ1NfQ0Q0X2RlbnNpdHknLCAnU19DRDIwX2RlbnNpdHknLCAnU19QbGFzbWFfZGVuc2l0eScpLCAibXV0c2lnX3RpbHR5cGVzIiA9IGMoJ0VfQ0Q4X2RlbnNpdHknLCAnRV9DRDRfZGVuc2l0eScsICdFX0NEMjBfZGVuc2l0eScsICdFX1BsYXNtYV9kZW5zaXR5JywgJ1NfQ0Q4X2RlbnNpdHknLCAnU19DRDRfZGVuc2l0eScsICdTX0NEMjBfZGVuc2l0eScsICdTX1BsYXNtYV9kZW5zaXR5JywgJ1RfQ0Q4X2RlbnNpdHknLCAnVF9DRDRfZGVuc2l0eScsICdUX0NEMjBfZGVuc2l0eScsICdUX1BsYXNtYV9kZW5zaXR5JyksICJpY2djX25vcm1hbGl6ZWRfcmVhZHNfbWF0cml4IiA9ICcvc2hhaGxhYi9hbHpoYW5nL2RhdGEvSUNHQy9PVkFVX2V4cHJfbWF0cml4LnRzdicsICJ4Y3JtYXBzY2FwZV9ub3RlYm9vayIgPSAnUm1kL3hjcm1hcHNjYXBlLlJtZCcsICJrbm93bl9zdWJ0eXBlX2ZpbGUiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9kYXRhL2V4cHJlc3Npb24vYXJyYXkvc3VidHlwZXMva25vd25fc3VidHlwZXMudHN2JywgInRjcl9jbG9ub3R5cGVzIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3BpcGVsaW5lX291dHB1dHMvaXRoX2ltbXVuZS9taXhjci9taXhjcl9ydW5zL2l0aF8xXzJfMy9taXhjcjUvY2xvbm90eXBlcy9UUkJfY2xvbm90eXBlc19maWx0ZXJlZC50eHQnLCAiY2xhc3NpZmllcl90eXBlIiA9ICdrbm4nLCAidGNnYV9leHByX21hdHJpeCIgPSAnL3NoYWhsYWIvYWx6aGFuZy9kYXRhL1RDR0EvZXhwcl9tYXRyaXhfbm9ybWFsaXplX3N0YW5kYXJkaXplX25vZHVwbGljYXRlcy50c3YnLCAiZmlndXJlX2dhbGxlcnlfbm90ZWJvb2siID0gJ1JtZC9maWd1cmVzLlJtZCcsICJjbG9uZV9icmFuY2hfbGVuZ3RoX2ZpbGUiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9kYXRhL2l0aC9jb21wbGV0ZS9icmFuY2hfZGF0YS50c3YnLCAid2FuZ19mYmlfc3RhdHVzIiA9ICcvc2hhaGxhYi9hbHpoYW5nL2RhdGEvSUNHQy9uZy4zODQ5LVMxMi50eHQnLCAiaWNnY19jbGluaWNhbCIgPSAnL3NoYWhsYWIvYWx6aGFuZy9kYXRhL0lDR0MvZG9ub3IuT1YtQVUudHN2JywgImloY19ydW4yIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvZGF0YS9paGMvY2Q3OWNkMTM4Y2Q2OC92YWxpZGF0ZWRfc3RhdHNfd2VpZ2h0ZWQucmRhdGEnLCAibmFub3N0cmluZ19hbm5vdGF0aW9ucyIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL2RhdGEvZXhwcmVzc2lvbi9uYW5vc3RyaW5nL3BhbmNhbmNlcl9hbm5vdGF0aW9ucy50c3YnLCAiaWNnY19zcGVjaW1lbl9maWxlIiA9ICcvc2hhaGxhYi9hbHpoYW5nL2RhdGEvSUNHQy9zcGVjaW1lbi50c3YnLCAicHJvcG9ydGlvbl9zdWJjbG9uYWxfZmlsZSIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL2RhdGEvaXRoL2NvbXBsZXRlL29sZF9wcm9wb3J0aW9uX3N1YmNsb25hbC50c3YnLCAibW9sc3VidHlwZV9ub3RlYm9vayIgPSAnUm1kL21vbGVjdWxhcl9zdWJ0eXBlcy5SbWQnLCAiZHJpdmVyX21hcCIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3N1YnByb2plY3RzL2RyaXZlcnMvZGF0YS9nZW5lX2xpc3RfbWFwcGVkLmJlZCcsICJqX2RpY3Rpb25hcnkiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9zdWJwcm9qZWN0cy9pbW10eXBlci9tZXRhZGF0YS9pbWd0L0hvbW9fc2FwaWVuc19UUkJKLmZhc3RhJywgImloY194Y3JfdGlsdHlwZXMiID0gYygnRV9DRDhfZGVuc2l0eScsICdFX0NENF9kZW5zaXR5JywgJ0VfQ0QyMF9kZW5zaXR5JywgJ0VfUGxhc21hX2RlbnNpdHknLCAnU19DRDhfZGVuc2l0eScsICdTX0NENF9kZW5zaXR5JywgJ1NfQ0QyMF9kZW5zaXR5JywgJ1NfUGxhc21hX2RlbnNpdHknLCAnVF9DRDhfZGVuc2l0eScsICdUX0NENF9kZW5zaXR5JywgJ1RfQ0QyMF9kZW5zaXR5JywgJ1RfUGxhc21hX2RlbnNpdHknKSwgInN1YnR5cGVfbWFya2VyX2ZpbGUiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9kYXRhL2V4cHJlc3Npb24vbmFub3N0cmluZy9zdWJ0eXBlX21hcmtlcnMudHN2JywgIm1tY3RtX292X2NvbWJpbmVkX3Jlc3VsdF9kaXIiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9yZXN1bHRzL21tY3RtX3Jlc3VsdHMvY29tYmluZWRfb3ZfbW1jdG0vb3V0cHV0JywgIml0aF9zdGF0c19maWxlIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvZGF0YS9pdGgvY29tcGxldGUvY2xvbmFsX21lYXN1cmVzLnRzdicsICJtb2xzdWJ0eXBlX3RpbHR5cGVzIiA9IGMoJ0VfQ0Q4X2RlbnNpdHknLCAnRV9DRDRfZGVuc2l0eScsICdFX0NEMjBfZGVuc2l0eScsICdFX1BsYXNtYV9kZW5zaXR5JywgJ1NfQ0Q4X2RlbnNpdHknLCAnU19DRDRfZGVuc2l0eScsICdTX0NEMjBfZGVuc2l0eScsICdTX1BsYXNtYV9kZW5zaXR5JyksICJ0aWxzX2Zvcl92YXJpYWJpbGl0eSIgPSBjKCdFX0NEOF9kZW5zaXR5JywgJ0VfQ0Q0X2RlbnNpdHknLCAnRV9DRDIwX2RlbnNpdHknLCAnRV9QbGFzbWFfZGVuc2l0eScsICdTX0NEOF9kZW5zaXR5JywgJ1NfQ0Q0X2RlbnNpdHknLCAnU19DRDIwX2RlbnNpdHknLCAnU19QbGFzbWFfZGVuc2l0eScpLCAic3BhdGlhbF9yZXN1bHRfZGlycyIgPSBsaXN0KCJlcGl0aGVsaWFsIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3BpcGVsaW5lX291dHB1dHMvaXRoX2ltbXVuZS9zcGF0c2ltL2l0aDMvYWJjJywgInN0cm9tYWwiID0gJy9zaGFobGFiL2FsemhhbmcvcGlwZWxpbmVfb3V0cHV0cy9pdGhfaW1tdW5lL3NwYXRzaW0vaXRoNS9hYmMnKSwgImtub3duX3N1YnR5cGVzX21lcmdlZCIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL2RhdGEvZXhwcmVzc2lvbi9rbm93bl9zdWJ0eXBlc19tZXJnZWQudHN2JywgIm1tY3RtX3BhdGllbnRfYWRfc2lncGxvdCIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3Jlc3VsdHMvbW1jdG1fcmVzdWx0cy9pdGhfYnktcGF0aWVudC1hbmNlc3RyeS9wbG90cy9pdGgtYnktcGF0aWVudC1hbmNlc3RyeV9zbnYtc3Zfc2lnc19tdWx0aXBhbmVsLnBkZicsICJpbmRleF9ub3RlYm9vayIgPSAnUm1kL2luZGV4LlJtZCcsICJtYXBzY2FwZV9ub3RlYm9vayIgPSAnUm1kL21hcHNjYXBlLlJtZCcsICJiY3JwaHlsb19leGFtcGxlc19ub3RlYm9vayIgPSAnUm1kL2Jjcl9waHlsb19leGFtcGxlcy5SbWQnKSwKICAgIHJ1bGUgPSAnbXV0YXRpb25fc2lnbmF0dXJlX25vdGVib29rJwopCiMjIyMjIyMjIE9yaWdpbmFsIHNjcmlwdCAjIyMjIyMjIyMKCiAgICAgICAgICAgICAgICAgICAgICAgIGBgYAoKCmBgYHtyIGdsb2JhbF9jaHVua19vcHRpb25zLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIHRpZHk9VFJVRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSkKYGBgCgoKYGBge3J9CmxpYnJhcnkoaXRoaS51dGlscykKbG9hZF9iYXNlX2xpYnMoKQpsaWJyYXJ5KHN1cnZpdmFsKQpsaWJyYXJ5KHJtcykKbGlicmFyeShlZGdlUikKbGlicmFyeShsaW1tYSkKbGlicmFyeShnYWdlKQpsaWJyYXJ5KHBhdGh2aWV3KQpsaWJyYXJ5KERUKQpsaWJyYXJ5KGFwZSkKbGlicmFyeShwaHl0b29scykKbGlicmFyeShwaHlsb2Jhc2UpCgpsaWJyYXJ5KGl0aGkubWV0YSkKbGlicmFyeShpdGhpLnhjcikKbGlicmFyeShpdGhpLmloYykKbGlicmFyeShpdGhpLnNlcSkKbGlicmFyeShpdGhpLmNsb25lcykKbGlicmFyeShpdGhpLmV4cHIpCmxpYnJhcnkoaXRoaS5leHRlcm5hbCkKYGBgCgojIyBDb2xvdXIgcGFsZXR0ZXMKCmBgYHtyfQpwYWxfcGF0aWVudCA8LSBzZWxlY3RfcGFsZXR0ZSgicGF0aWVudCIpCmBgYAoKIyMgUGFyYW1ldGVycwoKYGBge3J9CmRiX3BhdGggPC0gc25ha2VtYWtlQHBhcmFtcyRkYgoKbW1jdG1fc2FtcGxlX3Jlc3VsdF9kaXIgPC0gc25ha2VtYWtlQGlucHV0JG1tY3RtX3NhbXBsZV9kaXIKbW1jdG1fc2FtcGxlX2FkX3Jlc3VsdF9kaXIgPC0gc25ha2VtYWtlQGlucHV0JG1tY3RtX3NhbXBsZV9hZF9kaXIKbW1jdG1fcGF0aWVudF9hZF9yZXN1bHRfZGlyIDwtIHNuYWtlbWFrZUBpbnB1dCRtbWN0bV9wYXRpZW50X2FkX2RpcgptbWN0bV9vdl9jb21iaW5lZF9yZXN1bHRfZGlyIDwtIHNuYWtlbWFrZUBpbnB1dCRtbWN0bV9vdl9jb21iaW5lZF9kaXIKbW1jdG1fZmluYWxfcGF0aWVudF9kaXIgPC0gc25ha2VtYWtlQGlucHV0JG1tY3RtX2ZpbmFsX3BhdGllbnRfZGlyCgptbWN0bV9zYW1wbGVfc2lncGxvdCA8LSBzbmFrZW1ha2VAaW5wdXQkbW1jdG1fc2FtcGxlX3NpZ3Bsb3QKbW1jdG1fc2FtcGxlX2FkX3NpZ3Bsb3QgPC0gc25ha2VtYWtlQGlucHV0JG1tY3RtX3NhbXBsZV9hZF9zaWdwbG90Cm1tY3RtX3BhdGllbnRfYWRfc2lncGxvdCA8LSBzbmFrZW1ha2VAaW5wdXQkbW1jdG1fcGF0aWVudF9hZF9zaWdwbG90Cm1tY3RtX292X2NvbWJpbmVkX3NpZ3Bsb3QgPC0gc25ha2VtYWtlQGlucHV0JG1tY3RtX292X2NvbWJpbmVkX3NpZ3Bsb3QKbW1jdG1fZmluYWxfcGF0aWVudF9zaWdwbG90IDwtIHNuYWtlbWFrZUBpbnB1dCRtbWN0bV9maW5hbF9wYXRpZW50X3NpZ3Bsb3QKCm1hc3Rlcl92YXJpYW50X2ZpbGUgPC0gc25ha2VtYWtlQGlucHV0JG1hc3Rlcl92YXJpYW50X2ZpbGUKbWFzdGVyX2JyZWFrcG9pbnRfZmlsZSA8LSBzbmFrZW1ha2VAaW5wdXQkbWFzdGVyX2JyZWFrcG9pbnRfZmlsZQoKaWhjX3RhYmxlX3BhdGggPC0gc25ha2VtYWtlQGlucHV0JGloY190YWJsZQppaGNfdGFibGVfc2xpZGVfcGF0aCA8LSBzbmFrZW1ha2VAaW5wdXQkaWhjX3RhYmxlX3NsaWRlCnRpbHR5cGVzIDwtIHNuYWtlbWFrZUBwYXJhbXMkbXV0c2lnX3RpbHR5cGVzCgp0Y3JfZGl2ZXJzaXR5X2ZpbGUgPC0gc25ha2VtYWtlQGlucHV0JHRjcl9kaXZlcnNpdHkKYmNyX2RpdmVyc2l0eV9maWxlIDwtIHNuYWtlbWFrZUBpbnB1dCRiY3JfZGl2ZXJzaXR5CgpuYW5vc3RyaW5nX2RhdGFfcGF0aCA8LSBzbmFrZW1ha2VAaW5wdXQkbmFub3N0cmluZ19kYXRhCm5hbm9zdHJpbmdfYW5ub3RhdGlvbnNfcGF0aCA8LSBzbmFrZW1ha2VAaW5wdXQkbmFub3N0cmluZ19hbm5vdGF0aW9ucwoKbW9sZWN1bGFyX3N1YnR5cGVfZmlsZSA8LSBzbmFrZW1ha2VAaW5wdXQkbW9sZWN1bGFyX3N1YnR5cGVzCgppY2djX2V4cHJfbWF0X2ZpbGUgPC0gc25ha2VtYWtlQGlucHV0JGljZ2NfZXhwcl9tYXQKaWNnY19zcGVjaW1lbl9maWxlIDwtIHNuYWtlbWFrZUBpbnB1dCRpY2djX3NwZWNpbWVuCmljZ2Nfc3VidHlwZV9maWxlIDwtIHNuYWtlbWFrZUBpbnB1dCRpY2djX3N1YnR5cGVzCmljZ2NfZXhwcl9yYXdfZmlsZSA8LSBzbmFrZW1ha2VAaW5wdXQkaWNnY19leHByX21lbHRlZAppY2djX2Rvbm9yX2ZpbGUgPC0gc25ha2VtYWtlQGlucHV0JGljZ2NfY2xpbmljYWwKCnRjZ2FfZXhwcl9tYXRfZmlsZSA8LSBzbmFrZW1ha2VAaW5wdXQkdGNnYV9leHByX21hdAp0Y2dhX292X2Fubm90YXRpb25fZmlsZSA8LSBzbmFrZW1ha2VAaW5wdXQkdGNnYV9vdl9hbm5vdGF0aW9uCnRjZ2FfZG9ub3JfZmlsZSA8LSBzbmFrZW1ha2VAaW5wdXQkdGNnYV9jbGluaWNhbAoKcHJvcG9ydGlvbl9zdWJjbG9uYWxpdHlfZmlsZSA8LSBzbmFrZW1ha2VAaW5wdXQkc3ViY2xvbmFsaXR5Cgp3YW5nX2ljZ2NfZmJpX3N0YXR1c19maWxlIDwtIHNuYWtlbWFrZUBpbnB1dCR3YW5nX2ZiaV9zdGF0dXMKCnNudl9jbHVzdGVyX2ZpbGVzIDwtIHNuYWtlbWFrZUBpbnB1dCRzbnZfY2x1c3Rlcl9maWxlcwpzbnZfY2x1c3Rlcl9wYXRpZW50cyA8LSBzbmFrZW1ha2VAcGFyYW1zJHNudl9jbHVzdGVyX3BhdGllbnRzCgpjbG9uZV90cmVlX2ZpbGUgPC0gc25ha2VtYWtlQGlucHV0JGNsb25lX3RyZWVfZmlsZQpjbG9uZV9wcmV2YWxlbmNlX2ZpbGUgPC0gc25ha2VtYWtlQGlucHV0JGNsb25lX3ByZXZhbGVuY2VfZmlsZQpjbG9uZV9icmFuY2hfbGVuZ3RoX2ZpbGUgPC0gc25ha2VtYWtlQGlucHV0JGNsb25lX2JyYW5jaF9sZW5ndGhfZmlsZQoKaXRoX2ljZ2NfYmF0Y2hfY29ycmVjdGVkX2V4cHJlc3Npb25fZmlsZSA8LSBzbmFrZW1ha2VAaW5wdXQkaXRoX2ljZ2NfYmMKYGBgCgojIyBNZXRhZGF0YQoKYGBge3J9CmRiIDwtIHNyY19zcWxpdGUoZGJfcGF0aCwgY3JlYXRlPUZBTFNFKQpzYW1wbGVzIDwtIGNvbGxlY3QodGJsKGRiLCAic2FtcGxlcyIpKQpgYGAKCmBgYHtyfQpyZWFkX211dHNpZ19vdXRwdXQgPC0gZnVuY3Rpb24oZGlyLCBzYW1wbGVfdGFibGUsIGV4dGVybmFsPUZBTFNFKSB7CiAgcHJvcF90YWJsZV9wYXRoIDwtIFN5cy5nbG9iKGZpbGUucGF0aChkaXIsICIqX3Byb3BzLnRzdiIpKQogIHNpZ3NfcGF0aCA8LSBTeXMuZ2xvYihmaWxlLnBhdGgoZGlyLCAiLi4vcGxvdHMvKl9tdWx0aXBhbmVsLnBkZiIpKQogIAogIHByb3BfdGFibGUgPC0gZnJlYWQocHJvcF90YWJsZV9wYXRoKQogIAogIHNhbXBsZV9jb2xzIDwtIGNvbG5hbWVzKHByb3BfdGFibGUpW2NvbG5hbWVzKHByb3BfdGFibGUpICE9ICJzaWduYXR1cmUiXQogIGl0aF9jb2xzIDwtIHN0cl9kZXRlY3Qoc2FtcGxlX2NvbHMsICJwYXRpZW50IikKICAKICBwYXRpZW50cyA8LSBzdHJfZXh0cmFjdChzYW1wbGVfY29sc1tpdGhfY29sc10sICIoPzw9cGF0aWVudF8/KVswLTldKyIpCiAgc2l0ZV9pZHMgPC0gc3RyX3JlcGxhY2VfYWxsKHNhbXBsZV9jb2xzW2l0aF9jb2xzXSwgInBhdGllbnRfP1swLTldK18iLCAiIikKICAKICBGUk9NIDwtIGMoInJlY3R1bV9zaXRlXzEiLCAicmVjdHVtX3NpdGVfMiIsICJyZWN0dW1fc2l0ZV8zIiwgInJlY3R1bV9zaXRlXzQiLAogICAgICAgICAgICAicGVsdmlzX3NpdGVfMSIsICJwZWx2aXNfc2l0ZV8yIiwgImN1bF9kZV9zYWNfc2l0ZV8xIikKICBUTyA8LSBjKCJyZWN0dW1fc2l0ZV8xIiwgInJlY3R1bV9zaXRlXzEiLCAicmVjdHVtX3NpdGVfMiIsICJyZWN0dW1fc2l0ZV8yIiwKICAgICAgICAgICJwZWx2aXNfc2l0ZV8xIiwgInBlbHZpc19zaXRlXzEiLCAiY3VsLWRlLXNhY19zaXRlXzEiKQogIAogIHNpdGVfaWRzX2ZpeGVkIDwtIG1hcHZhbHVlcyhzaXRlX2lkcywgZnJvbSA9IEZST00sIHRvPVRPKQogIGRmIDwtIGRhdGEuZnJhbWUocGF0aWVudF9pZD1wYXRpZW50cywgc2l0ZV9pZD1zaXRlX2lkc19maXhlZCwgb2xkX2lkPXNhbXBsZV9jb2xzW2l0aF9jb2xzXSkKICBpZiAobGVuZ3RoKGRmW3dpdGgoZGYsIHBhdGllbnRfaWQgPT0gIjExIiAmIHNpdGVfaWQgPT0gImxlZnRfb3Zhcnlfc2l0ZV8yIiksXSRzaXRlX2lkKSA+IDApIHsKICAgIGRmW3dpdGgoZGYsIHBhdGllbnRfaWQgPT0gIjExIiAmIHNpdGVfaWQgPT0gImxlZnRfb3Zhcnlfc2l0ZV8yIiksXSRzaXRlX2lkIDwtICJsZWZ0X292YXJ5X3NpdGVfMyIKICB9CiAgCiAgc2FtcGxlX3N1YnNldCA8LSB1bmlxdWUoc3Vic2V0KHNhbXBsZV90YWJsZSwgc2VsZWN0PWMoImNvbmRlbnNlZF9pZCIsICJwYXRpZW50X2lkIiwgInNpdGVfaWQiKSkpCiAgCiAgZGYgPC0gcGx5cjo6am9pbihkZiwgc2FtcGxlX3N1YnNldCkKICBpZiAobnJvdyhkZikgPiAwKSB7CiAgICBkZiRpc19hbmNlc3RyYWwgPC0gMAogICAgZGYkaXNfYW5jZXN0cmFsW2RmJHNpdGVfaWQgPT0gImFuY2VzdHJhbCJdIDwtIDEKICB9CiAgCiAgI2NvbG5hbWVzKHByb3BfdGFibGUpW2l0aF9jb2xzXSA8LSBtYWtlLnVuaXF1ZShkZiRjb25kZW5zZWRfaWQpCiAgCiAgcHJvcF9maW5hbCA8LSBtZWx0KHByb3BfdGFibGUsIGlkLnZhcnMgPSBjKCdzaWduYXR1cmUnKSwgbWVhc3VyZS52YXJzID0gc2FtcGxlX2NvbHMsCiAgICAgICAgICAgICAgICAgICAgIHZhcmlhYmxlLm5hbWUgPSAib2xkX2lkIiwgdmFsdWUubmFtZSA9ICJwcm9wb3J0aW9uIikKICBpZiAoZXh0ZXJuYWwpIHsKICAgIHByb3BfZmluYWxfbWVyZ2VkIDwtIHBseXI6OmpvaW4oZGYsIHByb3BfZmluYWwsIGJ5PWMoIm9sZF9pZCIpLCB0eXBlPSdmdWxsJykKICB9IGVsc2UgewogICAgcHJvcF9maW5hbF9tZXJnZWQgPC0gcGx5cjo6am9pbihkZiwgcHJvcF9maW5hbCwgYnk9Yygib2xkX2lkIiksIHR5cGU9J2lubmVyJykKICB9CiAgCiAgcHJvcF9maW5hbF9tZXJnZWQgPC0gcHJvcF9maW5hbF9tZXJnZWQgJT4lIG11dGF0ZShuZXdfaWQgPSBpZmVsc2Uoc2l0ZV9pZCAlaW4lIGMoImFuY2VzdHJhbCIsICJyZXNpZHVhbCIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHllcyA9IHBhc3RlKHBhdGllbnRfaWQsIHNpdGVfaWQsIHNlcD0iXyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5vID0gY29uZGVuc2VkX2lkKSkKICBuYV9pZHggPC0gaXMubmEocHJvcF9maW5hbF9tZXJnZWQkbmV3X2lkKQogIHByb3BfZmluYWxfbWVyZ2VkJG5ld19pZFtuYV9pZHhdIDwtIGFzLmNoYXJhY3Rlcihwcm9wX2ZpbmFsX21lcmdlZCRvbGRfaWRbbmFfaWR4XSkKICAKICByZXN1bHQgPC0gbGlzdChwcm9wPXByb3BfZmluYWxfbWVyZ2VkLCBzaWdwbG90PXNpZ3NfcGF0aCkKICByZXR1cm4ocmVzdWx0KQp9CgpzdW1fdG90YWxfdmFyaWFudF9jb3VudHMgPC0gZnVuY3Rpb24odmFyaWFudF9zYW1wbGUpIHsKICB2YXJpYW50X3NhbXBsZSAlPiUgZ3JvdXBfYnkoY29uZGVuc2VkX2lkLCBwYXRpZW50X2lkKSAlPiUgCiAgICBzdW1tYXJpc2Uoc252X2NvdW50PXN1bShzbnZfY291bnQpLCBia3B0X2NvdW50PXN1bShia3B0X2NvdW50KSkKfQoKY29tcHV0ZV9tdXRzaWdfY291bnRzIDwtIGZ1bmN0aW9uKGRmKSB7CiAgZGYgPC0gZGYgJT4lIG11dGF0ZShpc19zdiA9IGFzLm51bWVyaWMoc3RyX2RldGVjdChzaWduYXR1cmUsICJeU1YiKSksCiAgICAgICAgICAgICAgICBzaWdjb3VudCA9IGlmZWxzZShhcy5jaGFyYWN0ZXIoc2l0ZV9pZCkgPT0gImFuY2VzdHJhbCIsCiAgICAgICAgICAgICAgICAgIHllcyA9IGlmZWxzZShpc19zdiwgeWVzID0gYW5jZXN0cmFsX2JrcHQqcHJvcG9ydGlvbiwgbm8gPSBhbmNlc3RyYWxfc252KnByb3BvcnRpb24pLAogICAgICAgICAgICAgICAgICBubyA9IGlmZWxzZShpc19zdiwgeWVzPWJrcHRfY291bnQgKiBwcm9wb3J0aW9uLCBubyA9IHNudl9jb3VudCAqIHByb3BvcnRpb24pKSkKICByZXR1cm4oZGYpCn0KCmNyZWF0ZV9tdXRzaWdfbWF0cml4IDwtIGZ1bmN0aW9uKGRmLCBjb2wgPSAicHJvcG9ydGlvbiIpIHsKICBtYXQgPC0gYWNhc3QoZGYsIG5ld19pZCB+IHNpZ25hdHVyZSwgZnVuLmFnZ3JlZ2F0ZSA9IG1lYW4sIHZhbHVlLnZhciA9IGNvbCkKICByZXR1cm4obWF0KQp9CmBgYAoKYGBge3J9CnZhcmlhbnRfcmVzIDwtIHN1bW1hcml6ZV92YXJpYW50X2NvdW50cyhtYXN0ZXJfdmFyaWFudF9maWxlLCBtYXN0ZXJfYnJlYWtwb2ludF9maWxlLCBkYl9wYXRoKQp2YXJpYW50X3NhbXBsZSA8LSB2YXJpYW50X3JlcyRzYW1wbGUKdmFyaWFudF9wYXRpZW50IDwtIHZhcmlhbnRfcmVzJHBhdGllbnQKCnZhcmlhbnRfc2FtcGxlX3N1bSA8LSBzdW1fdG90YWxfdmFyaWFudF9jb3VudHModmFyaWFudF9zYW1wbGUpCgppaGNfdGFibGUgPC0gZnJlYWQoaWhjX3RhYmxlX3BhdGgpCmloY190YWJsZV9zdWJzZXQgPC0gc3Vic2V0KGloY190YWJsZSwgc2VsZWN0PWMoImNvbmRlbnNlZF9pZCIsIHRpbHR5cGVzKSkKaWhjX3RhYmxlX3NsaWRlIDwtIGZyZWFkKGloY190YWJsZV9zbGlkZV9wYXRoKQpgYGAKCiMjIFNhbXBsZSBsZXZlbAoKIyMjIFNpZ25hdHVyZXMKCiFbXShgciBtbWN0bV9zYW1wbGVfc2lncGxvdGApCgoKIyMjIFJlc3VsdHMKCmBgYHtyfQpwcm9wc19zYW1wbGUgPC0gcmVhZF9tdXRzaWdfb3V0cHV0KG1tY3RtX3NhbXBsZV9yZXN1bHRfZGlyLCBzYW1wbGVzKQpwcm9wc19zYW1wbGVfcHJvcCA8LSBzdWJzZXQocHJvcHNfc2FtcGxlJHByb3AsIHBhdGllbnRfaWQgIT0gIjExIikKCnByb3BzX3NhbXBsZV9tYXQgPC0gY3JlYXRlX211dHNpZ19tYXRyaXgocHJvcHNfc2FtcGxlX3Byb3AsIGNvbCA9ICJwcm9wb3J0aW9uIikKCnByb3BzX3NhbXBsZV9tYXRfc2NhbGVkIDwtIGNsaXBfdmFsdWVzKHNjYWxlKHByb3BzX3NhbXBsZV9tYXQpLCAyLCAtMikKc2FtcGxlX2hlYXQgPC0gcGhlYXRtYXAocHJvcHNfc2FtcGxlX21hdF9zY2FsZWQsIGZvbnRzaXplX3JvdyA9IDUsIGNsdXN0ZXJpbmdfZGlzdGFuY2Vfcm93cyA9ICJjb3JyZWxhdGlvbiIsIGNsdXN0ZXJpbmdfbWV0aG9kID0gIndhcmQuRDIiKQpgYGAKCmBgYHtyfQpwcm9wc19zYW1wbGVfbWVyZ2VkIDwtIFJlZHVjZShmdW5jdGlvbih4LHkpIHBseXI6OmpvaW4oeCx5KSxsaXN0KHByb3BzX3NhbXBsZV9wcm9wLCB2YXJpYW50X3NhbXBsZV9zdW0sIHZhcmlhbnRfcGF0aWVudCwgaWhjX3RhYmxlX3N1YnNldCkpCgpwcm9wc19zYW1wbGVfbWVyZ2VkIDwtIGNvbXB1dGVfbXV0c2lnX2NvdW50cyhwcm9wc19zYW1wbGVfbWVyZ2VkKQpgYGAKCmBgYHtyfQpDT0wgPC0gIkVfQ0Q4X2RlbnNpdHkiCm1lYXN1cmUgPC0gInNpZ2NvdW50IgoKcHZhbHMgPC0gc2V0TmFtZXMoZGRwbHkocHJvcHNfc2FtcGxlX21lcmdlZCwgLihzaWduYXR1cmUpLCBmdW5jdGlvbih4KSB7CiAgZGYgPC0gYXMuZGF0YS5mcmFtZSh4KQogIGRmIDwtIGRmWyFpcy5uYShkZlssQ09MXSksXQogIGNvcnJlcyA8LSBzdW1tYXJ5KGxtZXIoYXMuZm9ybXVsYShwYXN0ZTAobWVhc3VyZSwgIiB+ICIsIENPTCwgIiArICgxfHBhdGllbnRfaWQpIikpLCBkZikpCiAgCiAgcHZhbCA8LSB1bm5hbWUoY29ycmVzJGNvZWZmaWNpZW50c1ssNV1bMl0pCiAgZXEgPC0gc3Vic3RpdHV0ZShpdGFsaWMoUCk9PXAsIGxpc3QocD1mb3JtYXQocHZhbCwgZGlnaXRzPTMpKSkKICByZXR1cm4oYXMuY2hhcmFjdGVyKGFzLmV4cHJlc3Npb24oZXEpKSkKfSksIGMoInNpZ25hdHVyZSIsICJwLnZhbHVlIikpCgpnZ3Bsb3QocHJvcHNfc2FtcGxlX21lcmdlZCwgYWVzX3N0cmluZyh4PUNPTCwgeT1tZWFzdXJlKSkgKyBnZW9tX3BvaW50KGFlcyhjb2xvdXI9cGF0aWVudF9pZCkpICsgCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBwYWxfcGF0aWVudCkgKyBmYWNldF93cmFwKH4gc2lnbmF0dXJlLCBzY2FsZXM9ImZyZWUiKSArIAogIGdlb21fdGV4dChkYXRhPXB2YWxzLCBhZXMoeD1JbmYsIHk9SW5mLCBsYWJlbD1wLnZhbHVlKSwgaGp1c3Q9MS4xLCB2anVzdD0xLjUsc2l6ZT0zLHBhcnNlPVRSVUUpICsgCiAgdGhlbWVfYncoKQpgYGAKCmBgYHtyfQojIHggPC0gUmVkdWNlKGZ1bmN0aW9uKHgseSkgbWVyZ2UoeCx5LCBieT1jKCJjb25kZW5zZWRfaWQiKSksIGxpc3Qocm93bmFtZXNfdG9fY29sdW1uKGFzLmRhdGEuZnJhbWUodChwcm9wX21hdHJpeCkpLCB2YXIgPSAiY29uZGVuc2VkX2lkIiksIAojICAgICAgICAgICAgaWhjX3RhYmxlX3N1YnNldCkpCiMgeCRwYXRpZW50X2lkIDwtIG1hcF9pZCh4JGNvbmRlbnNlZF9pZCwgZnJvbSA9ICJjb25kZW5zZWRfaWQiLCB0bz0icGF0aWVudF9pZCIsIGRiX3BhdGgpCiMgeCA8LSBzdWJzZXQoeCwgcGF0aWVudF9pZCAhPSAiMTEiKQojIAojIGNvbHMgPC0gY29sbmFtZXMoeClbIWNvbG5hbWVzKHgpICVpbiUgYygiY29uZGVuc2VkX2lkIiwgInBhdGllbnRfaWQiKV0KIyBybWF0IDwtIG1hdHJpeChucm93PWxlbmd0aChjb2xzKSxuY29sPWxlbmd0aChjb2xzKSkKIyBwbWF0IDwtIG1hdHJpeChucm93PWxlbmd0aChjb2xzKSxuY29sPWxlbmd0aChjb2xzKSkKIyAKIyBmb3IgKGkgaW4gMTpsZW5ndGgoY29scykpIHsKIyAgIGZvciAoaiBpbiBpOmxlbmd0aChjb2xzKSkgewojICAgICBpZiAoaSAhPSBqKSB7CiMgICAgICAgY29sMSA8LSBjb2xzW2ldCiMgICAgICAgY29sMiA8LSBjb2xzW2pdCiMgICAgICAgZm9ybXVsYSA8LSBwYXN0ZTAoImAiLCBjb2wyLCAiYCIsICJ+IiwgImAiLCBjb2wxLCAiYCIsICIrICgxfHBhdGllbnRfaWQpIiwgc2VwPSIgIikKIyAgICAgICByZXMgPC0gc3VtbWFyeShsbWVyKGZvcm11bGEsIHgpKQojICAgICAgIHB2YWwgPC0gdHJ5Q2F0Y2goewojICAgICAgICAgdW5uYW1lKHJlcyRjb2VmZmljaWVudHNbLDVdWzJdKQojICAgICAgIH0sIGVycm9yID0gZnVuY3Rpb24oZSkgewojICAgICAgICAgTkEKIyAgICAgICB9KQojICAgICAgIHIgPC0gdW5uYW1lKHJlcyRjb2VmZmljaWVudHNbLDFdWzJdKQojICAgICAgIHJtYXRbaSxqXSA8LSBybWF0W2osaV0gPC0gcgojICAgICAgIHBtYXRbaSxqXSA8LSBwbWF0W2osaV0gPC0gcHZhbAojICAgICB9IGVsc2UgewojICAgICAgIHJtYXRbaSxqXSA8LSAxCiMgICAgICAgcG1hdFtpLGpdIDwtIE5BCiMgICAgIH0KIyAgIH0KIyB9CgpgYGAKCmBgYHtyfQojIHJlc21hdCA8LSBsb2cxMChwbWF0KSotc2lnbihybWF0KQojIHJlc21hdFtyZXNtYXQgPT0gSW5mXSA8LSBOQQojIHJvd25hbWVzKHJlc21hdCkgPC0gY29sbmFtZXMocmVzbWF0KSA8LSBjb2xzCiMgCiMgcGhlYXRtYXAocmVzbWF0LCBkaXNwbGF5X251bWJlcnMgPSBzaWduaWYocG1hdCwgMyksIGNsdXN0ZXJfcm93cyA9IEZBTFNFLCBjbHVzdGVyX2NvbHMgPSBGQUxTRSwgZm9udHNpemUgPSA1KQpgYGAKCmBgYHtyfQojIHhtYXQgPC0geCAlPiUgc2VsZWN0KC1vbmVfb2YoImNvbmRlbnNlZF9pZCIsICJwYXRpZW50X2lkIikpCiMgCiMgY29ybWF0IDwtIGNvcnIudGVzdCh4bWF0LCBtZXRob2Q9InNwZWFybWFuIiwgYWRqdXN0PSJmZHIiKQojIHBoZWF0bWFwKGNvcm1hdCRyLCBkaXNwbGF5X251bWJlcnMgPSBzaWduaWYoY29ybWF0JHAsIDMpLCBmb250c2l6ZSA9IDUpCmBgYAoKCiMjIEFuY2VzdHJhbC1kZXNjZW5kYW50IGxldmVsIChzYW1wbGVzKQoKIyMjIFNpZ25hdHVyZXMKCiFbXShgciBtbWN0bV9zYW1wbGVfYWRfc2lncGxvdGApCgojIyMgUmVzdWx0cwoKYGBge3J9CnByb3BzX2FkIDwtIHJlYWRfbXV0c2lnX291dHB1dChtbWN0bV9zYW1wbGVfYWRfcmVzdWx0X2Rpciwgc2FtcGxlcykKcHJvcHNfYWRfcHJvcCA8LSBzdWJzZXQocHJvcHNfYWQkcHJvcCwgcGF0aWVudF9pZCAhPSAiMTEiKQoKcHJvcHNfYWRfbWF0IDwtIGNyZWF0ZV9tdXRzaWdfbWF0cml4KHByb3BzX2FkX3Byb3AsIGNvbCA9ICJwcm9wb3J0aW9uIikKCnByb3BzX2FkX21hdF9zY2FsZWQgPC0gY2xpcF92YWx1ZXMoc2NhbGUocHJvcHNfYWRfbWF0KSwgMiwgLTIpCnBhdGllbnRfaGVhdCA8LSBwaGVhdG1hcChwcm9wc19hZF9tYXRfc2NhbGVkLCBmb250c2l6ZV9yb3cgPSA1LCBjbHVzdGVyaW5nX2Rpc3RhbmNlX3Jvd3MgPSAiY29ycmVsYXRpb24iKQpgYGAKCmBgYHtyfQpwcm9wc19hZF9tZXJnZWQgPC0gUmVkdWNlKGZ1bmN0aW9uKHgseSkgcGx5cjo6am9pbih4LHkpLGxpc3QocHJvcHNfYWRfcHJvcCwgc3Vic2V0KHZhcmlhbnRfc2FtcGxlLCBpc19hbmNlc3RyYWwgPT0gMCksIHZhcmlhbnRfcGF0aWVudCwgaWhjX3RhYmxlX3N1YnNldCkpCgpwcm9wc19hZF9tZXJnZWQgPC0gY29tcHV0ZV9tdXRzaWdfY291bnRzKHByb3BzX2FkX21lcmdlZCkKYGBgCgpgYGB7cn0KQ09MIDwtICJFX0NEOF9kZW5zaXR5IgptZWFzdXJlIDwtICJzaWdjb3VudCIKCnByb3BzX2FkX21lcmdlZF9kZXNjZW5kYW50IDwtIHN1YnNldChwcm9wc19hZF9tZXJnZWQsIHNpdGVfaWQgIT0gImFuY2VzdHJhbCIpCgpwdmFscyA8LSBzZXROYW1lcyhkZHBseShwcm9wc19hZF9tZXJnZWRfZGVzY2VuZGFudCwgLihzaWduYXR1cmUpLCBmdW5jdGlvbih4KSB7CiAgZGYgPC0gYXMuZGF0YS5mcmFtZSh4KQogIGRmIDwtIGRmWyFpcy5uYShkZlssQ09MXSksXQogIGNvcnJlcyA8LSBzdW1tYXJ5KGxtZXIoYXMuZm9ybXVsYShwYXN0ZTAobWVhc3VyZSwgIiB+ICIsIENPTCwgIiArICgxfHBhdGllbnRfaWQpIikpLCBkZikpCiAgCiAgcHZhbCA8LSB1bm5hbWUoY29ycmVzJGNvZWZmaWNpZW50c1ssNV1bMl0pCiAgZXEgPC0gc3Vic3RpdHV0ZShpdGFsaWMoUCk9PXAsIGxpc3QocD1mb3JtYXQocHZhbCwgZGlnaXRzPTMpKSkKICByZXR1cm4oYXMuY2hhcmFjdGVyKGFzLmV4cHJlc3Npb24oZXEpKSkKfSksIGMoInNpZ25hdHVyZSIsICJwLnZhbHVlIikpCgpnZ3Bsb3QocHJvcHNfYWRfbWVyZ2VkX2Rlc2NlbmRhbnQsIGFlc19zdHJpbmcoeD1DT0wsIHk9bWVhc3VyZSkpICsgZ2VvbV9wb2ludChhZXMoY29sb3VyPXBhdGllbnRfaWQpKSArIAogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gcGFsX3BhdGllbnQpICsgZmFjZXRfd3JhcCh+IHNpZ25hdHVyZSwgc2NhbGVzPSJmcmVlIikgKyAKICBnZW9tX3RleHQoZGF0YT1wdmFscywgYWVzKHg9SW5mLCB5PUluZiwgbGFiZWw9cC52YWx1ZSksIGhqdXN0PTEuMSwgdmp1c3Q9MS41LHNpemU9MyxwYXJzZT1UUlVFKSArIAogIHRoZW1lX2J3KCkKYGBgCgpXaGF0IHdlIGNhbiBhbHNvIHNlZSBmcm9tIHRoZXNlIHBsb3RzIGlzIHRoYXQgbm90IHJlc29sdmluZyBTTlYtNyAodGhlIG5vaXNlIGNsdXN0ZXIpIGlzIGdvaW5nIHRvIGJlIGEgcHJvYmxlbS4gVGhlIHNpZ25hdHVyZSBwcm9wb3J0aW9uIHZhbHVlcyBmcm9tIHRoYXQgY2x1c3RlciBhcmUgZmFpcmx5IHNpbWlsYXIgdG8gdGhvc2UgZnJvbSBTTlYtNiwgdGhlIEFQT0JFQyBzaWduYXR1cmUuIAoKCiMjIEFuY2VzdHJhbC1kZXNjZW5kYW50IGxldmVsIChwYXRpZW50cykKCiMjIyBTaWduYXR1cmVzCgohW10oYHIgbW1jdG1fcGF0aWVudF9hZF9zaWdwbG90YCkKCiMjIyBSZXN1bHRzCgpgYGB7cn0KcHJvcHNfcGF0aWVudF9hZCA8LSByZWFkX211dHNpZ19vdXRwdXQobW1jdG1fcGF0aWVudF9hZF9yZXN1bHRfZGlyLCBzYW1wbGVzKQpwcm9wc19wYXRpZW50X2FkX3Byb3AgPC0gc3Vic2V0KHByb3BzX3BhdGllbnRfYWQkcHJvcCwgcGF0aWVudF9pZCAhPSAiMTEiKQoKcHJvcHNfcGF0aWVudF9hZF9tYXQgPC0gY3JlYXRlX211dHNpZ19tYXRyaXgocHJvcHNfcGF0aWVudF9hZF9wcm9wLCBjb2wgPSAicHJvcG9ydGlvbiIpCgpwcm9wc19wYXRpZW50X2FkX21hdF9zY2FsZWQgPC0gY2xpcF92YWx1ZXMoc2NhbGUocHJvcHNfcGF0aWVudF9hZF9tYXQpLCAyLCAtMikKcGF0aWVudF9hZF9oZWF0IDwtIHBoZWF0bWFwKHByb3BzX3BhdGllbnRfYWRfbWF0X3NjYWxlZCwgZm9udHNpemVfcm93ID0gNSwgY2x1c3RlcmluZ19kaXN0YW5jZV9yb3dzID0gImNvcnJlbGF0aW9uIikKYGBgCgpgYGB7cn0KcHJvcHNfcGF0aWVudF9hZF9tZXJnZWQgPC0gUmVkdWNlKGZ1bmN0aW9uKHgseSkgcGx5cjo6am9pbih4LHkpLGxpc3QocHJvcHNfcGF0aWVudF9hZF9wcm9wLCBzdWJzZXQodmFyaWFudF9zYW1wbGUsIGlzX2FuY2VzdHJhbCA9PSAwKSwgdmFyaWFudF9wYXRpZW50KSkKCnByb3BzX3BhdGllbnRfYWRfbWVyZ2VkIDwtIGNvbXB1dGVfbXV0c2lnX2NvdW50cyhwcm9wc19wYXRpZW50X2FkX21lcmdlZCkKYGBgCgoKIyMgQ29ycmVsYXRpb24gbWF0cmljZXMKCmBgYHtyfQppaGNfbWF0IDwtIGFzLmRhdGEuZnJhbWUoaWhjX3RhYmxlX3N1YnNldCAlPiUgZHBseXI6OnNlbGVjdCgtY29uZGVuc2VkX2lkKSkKcm93bmFtZXMoaWhjX21hdCkgPC0gaWhjX3RhYmxlX3N1YnNldCRjb25kZW5zZWRfaWQKCiNUT1RBTF9USUxfQ09MUyA8LSBjKCJUX0NEOF9kZW5zaXR5IiwgIlRfQ0Q0X2RlbnNpdHkiLCAiVF9DRDIwX2RlbnNpdHkiLCAiVF9QbGFzbWFfZGVuc2l0eSIpCiNpaGNfbWF0IDwtIHN1YnNldChpaGNfbWF0LCBzZWxlY3Q9VE9UQUxfVElMX0NPTFMpCmBgYAoKYGBge3J9CmNvbXB1dGVfaWhjX211dHNpZ19jb3IgPC0gZnVuY3Rpb24oaWhjX21hdCwgbXV0c2lnX21hdCwgcGF0aWVudF9zdW1tYXJ5ID0gRkFMU0UsIGFuY2VzdHJhbCA9IEZBTFNFKSB7CiAgaWYgKHBhdGllbnRfc3VtbWFyeSkgewogICAgaWhjX21hdCA8LSByb3duYW1lc190b19jb2x1bW4oYXMuZGF0YS5mcmFtZShpaGNfbWF0KSwgImlkIikKICAgIG11dHNpZ19tYXQgPC0gcm93bmFtZXNfdG9fY29sdW1uKGFzLmRhdGEuZnJhbWUobXV0c2lnX21hdCksICJpZCIpCiAgICAKICAgIGlmIChhbmNlc3RyYWwpIHsKICAgICAgbXV0c2lnX21hdCA8LSBzdWJzZXQobXV0c2lnX21hdCwgc3RyX2RldGVjdChpZCwgImFuY2VzdHJhbCIpKQogICAgfSBlbHNlIHsKICAgICAgbXV0c2lnX21hdCA8LSBzdWJzZXQobXV0c2lnX21hdCwgIXN0cl9kZXRlY3QoaWQsICJhbmNlc3RyYWwiKSkKICAgIH0KICAgIAogICAgaWhjX21hdCRwYXRpZW50X2lkIDwtIHN0cl9leHRyYWN0KGloY19tYXQkaWQsICJeWzAtOV0rIikKICAgIG11dHNpZ19tYXQkcGF0aWVudF9pZCA8LSBzdHJfZXh0cmFjdChtdXRzaWdfbWF0JGlkLCAiXlswLTldKyIpCiAgICAKICAgIGloY19tYXQgPC0gaWhjX21hdCAlPiUgZHBseXI6OnNlbGVjdCgtaWQpICU+JSBncm91cF9ieShwYXRpZW50X2lkKSAlPiUgc3VtbWFyaXNlX2VhY2goZnVucyhtZWFuKC4sIG5hLnJtPVRSVUUpKSkgJT4lIGNvbHVtbl90b19yb3duYW1lcyh2YXIgPSAicGF0aWVudF9pZCIpCiAgICBtdXRzaWdfbWF0IDwtIG11dHNpZ19tYXQgJT4lIGRwbHlyOjpzZWxlY3QoLWlkKSAlPiUgZ3JvdXBfYnkocGF0aWVudF9pZCkgJT4lIHN1bW1hcmlzZV9lYWNoKGZ1bnMobWVhbiguLCBuYS5ybT1UUlVFKSkpICU+JSBjb2x1bW5fdG9fcm93bmFtZXModmFyID0gInBhdGllbnRfaWQiKQogICAgCiAgICBpaGNfbWF0IDwtIGFzLm1hdHJpeChpaGNfbWF0KQogICAgbXV0c2lnX21hdCA8LSBhcy5tYXRyaXgobXV0c2lnX21hdCkKICB9CiAgCiAgaW50ZXJzZWN0X3NhbXBsZXMgPC0gaW50ZXJzZWN0KHJvd25hbWVzKG11dHNpZ19tYXQpLCByb3duYW1lcyhpaGNfbWF0KSkKICBtdXRzaWcgPC0gbXV0c2lnX21hdFtpbnRlcnNlY3Rfc2FtcGxlcywsZHJvcD1GQUxTRV0KICBpaGMgPC0gaWhjX21hdFtpbnRlcnNlY3Rfc2FtcGxlcywsZHJvcD1GQUxTRV0KICAKICBwbWF0IDwtIG1hdHJpeChucm93PW5jb2wobXV0c2lnKSwgbmNvbD1uY29sKGloYykpCiAgcm1hdCA8LSBtYXRyaXgobnJvdz1uY29sKG11dHNpZyksIG5jb2w9bmNvbChpaGMpKQogIHJvd25hbWVzKHBtYXQpIDwtIHJvd25hbWVzKHJtYXQpIDwtIGNvbG5hbWVzKG11dHNpZykKICBjb2xuYW1lcyhwbWF0KSA8LSBjb2xuYW1lcyhybWF0KSA8LSBjb2xuYW1lcyhpaGMpCiAgCiAgZm9yIChpIGluIDE6bmNvbChtdXRzaWcpKSB7CiAgICBmb3IgKGogaW4gMTpuY29sKGloYykpIHsKICAgICAgY29ycmVzIDwtIGNvci50ZXN0KG11dHNpZ1ssaV0sIGloY1ssal0sIG1ldGhvZD0ic3BlYXJtYW4iKQogICAgICBwbWF0W2ksal0gPC0gY29ycmVzJHAudmFsdWUKICAgICAgcm1hdFtpLGpdIDwtIGNvcnJlcyRlc3RpbWF0ZQogICAgfQogIH0KICAKICByZXR1cm4obGlzdChwPXBtYXQsIHI9cm1hdCkpCn0KYGBgCgpGb3IgYWRqdXN0ZWQgcC12YWx1ZXMganVzdCBydW4gYHAuYWRqdXN0Lm1hdGAgb24gdGhlIHAtdmFsdWUgbGFiZWxzLiAKCiMjIyBTYW1wbGUtbGV2ZWwKCmBgYHtyfQppaGNfc2FtcGxlX2NvciA8LSBjb21wdXRlX2loY19tdXRzaWdfY29yKGloY19tYXQsIHByb3BzX3NhbXBsZV9tYXQpCgpwaGVhdG1hcChpaGNfc2FtcGxlX2NvciRyLCBkaXNwbGF5X251bWJlcnMgPSBzaWduaWYoaWhjX3NhbXBsZV9jb3IkcCwgMykpCgppaGNfc2FtcGxlX2Nvcl9zdW1tYXJ5IDwtIGNvbXB1dGVfaWhjX211dHNpZ19jb3IoaWhjX21hdCwgcHJvcHNfc2FtcGxlX21hdCwgcGF0aWVudF9zdW1tYXJ5ID0gVFJVRSkKCnBoZWF0bWFwKGloY19zYW1wbGVfY29yX3N1bW1hcnkkciwgZGlzcGxheV9udW1iZXJzID0gc2lnbmlmKGloY19zYW1wbGVfY29yX3N1bW1hcnkkcCwgMykpCmBgYAoKIyMjIEFuY2VzdHJhbC1kZXNjZW5kYW50IGxldmVsCgpgYGB7cn0KaWhjX2FkX2NvciA8LSBjb21wdXRlX2loY19tdXRzaWdfY29yKGloY19tYXQsIHByb3BzX2FkX21hdCkKCnBoZWF0bWFwKGloY19hZF9jb3IkciwgZGlzcGxheV9udW1iZXJzID0gc2lnbmlmKGloY19hZF9jb3IkcCwgMykpCgppaGNfYWRfY29yX3N1bW1hcnkgPC0gY29tcHV0ZV9paGNfbXV0c2lnX2NvcihpaGNfbWF0LCBwcm9wc19hZF9tYXQsIHBhdGllbnRfc3VtbWFyeSA9IFRSVUUpCgpwaGVhdG1hcChpaGNfYWRfY29yX3N1bW1hcnkkciwgZGlzcGxheV9udW1iZXJzID0gc2lnbmlmKGloY19hZF9jb3Jfc3VtbWFyeSRwLCAzKSkKCmloY19hZF9jb3Jfc3VtbWFyeV9hbmNlc3RyYWwgPC0gY29tcHV0ZV9paGNfbXV0c2lnX2NvcihpaGNfbWF0LCBwcm9wc19hZF9tYXQsIHBhdGllbnRfc3VtbWFyeSA9IFRSVUUsIGFuY2VzdHJhbCA9IFRSVUUpCgpwaGVhdG1hcChpaGNfYWRfY29yX3N1bW1hcnlfYW5jZXN0cmFsJHIsIGRpc3BsYXlfbnVtYmVycyA9IHNpZ25pZihpaGNfYWRfY29yX3N1bW1hcnlfYW5jZXN0cmFsJHAsIDMpKQpgYGAKCk5vdGU6IHAtdmFsdWVzIHNob3duIGFyZSB1bmNvcnJlY3RlZC4gUGF0aWVudC1zdW1tYXJpemVkIGNvcnJlbGF0aW9ucyBhcmUgaW5zaWduaWZpY2FudCBhZnRlciBGRFIgY29ycmVjdGlvbi4gCgojIyBDbHVzdGVyLWxldmVsIGFuYWx5c2lzCgpGaW5kaW5nIGNvcnJlbGF0aW9ucyBhdCB0aGUgbGV2ZWwgb2YgaW5kaXZpZHVhbCBzaWduYXR1cmVzIGNhbiBiZSBkaWZmaWN1bHQuIEV2ZW4gbW9yZXNvIGJlY2F1c2Ugc29tZSBwdWJsaXNoZWQgc2lnbmF0dXJlcyBhcmUgY29tYmluYXRpb25zIG9mIHRoZXNlIHNpZ25hdHVyZXMgLS0gZS5nLiBTVi0zIGFuZCBTVi02IGFyZSBib3RoIGZvbGRiYWNrIHNpZ25hdHVyZXMgaW4gdGhlIGFuY2VzdHJhbC1kZXNjZW5kYW50IGFuYWx5c2lzLiAKCkhlbmNlLCB3ZSBjYW4gbG9vayBhdCB0aGUgbGV2ZWwgb2YgY2x1c3RlcnMgZnJvbSBvdXIgaGVhdG1hcHMuIAoKYGBge3J9Cm1ha2VfY2x1c3Rlcl9mcmFtZSA8LSBmdW5jdGlvbihjbHVzdGVycykgewogIGNsdXN0cyA8LSByb3duYW1lc190b19jb2x1bW4oYXMuZGF0YS5mcmFtZShjbHVzdGVycyksICJuZXdfaWQiKQogIGNvbG5hbWVzKGNsdXN0cylbMl0gPC0gImNsdXN0ZXIiCiAgY2x1c3RzJGNsdXN0ZXIgPC0gZmFjdG9yKGNsdXN0cyRjbHVzdGVyKQogIHJldHVybihjbHVzdHMpCn0KCk5DTFVTVCA8LSAyCnNlbGVjdGVkX2NvbHMgPC0gYygibmV3X2lkIiwgImNvbmRlbnNlZF9pZCIsICJwYXRpZW50X2lkIiwgIm9sZF9pZCIsICJjbHVzdGVyIiwgdGlsdHlwZXMpCmBgYAoKIyMjIFNhbXBsZS1sZXZlbAoKYGBge3J9CmNsdXN0ZXJzIDwtIGN1dHJlZShzYW1wbGVfaGVhdCR0cmVlX3JvdywgTkNMVVNUKQpzYW1wbGVfY2x1c3RzIDwtIG1ha2VfY2x1c3Rlcl9mcmFtZShjbHVzdGVycykKCnByb3BzX3NhbXBsZV9tZXJnZWRfY2x1c3RzIDwtIGpvaW4ocHJvcHNfc2FtcGxlX21lcmdlZCwgc2FtcGxlX2NsdXN0cykKc2FtcGxlX2RmIDwtIHVuaXF1ZShzdWJzZXQocHJvcHNfc2FtcGxlX21lcmdlZF9jbHVzdHMsIHNlbGVjdD1zZWxlY3RlZF9jb2xzKSkKCnNhbXBsZV9kZl9tZWx0ZWQgPC0gbWVsdChzYW1wbGVfZGYsIGlkLnZhcnMgPSBjb2xuYW1lcyhzYW1wbGVfZGYpWyFjb2xuYW1lcyhzYW1wbGVfZGYpICVpbiUgdGlsdHlwZXNdLCBtZWFzdXJlLnZhcnMgPSB0aWx0eXBlcywgdmFyaWFibGUubmFtZSA9ICJ0aWx0eXBlIiwgdmFsdWUubmFtZSA9ICJkZW5zaXR5IikKYGBgCgpgYGB7cn0KcHZhbHMgPC0gc2V0TmFtZXMoZGRwbHkoc2FtcGxlX2RmX21lbHRlZCwgLih0aWx0eXBlKSwgZnVuY3Rpb24oeCkgewogIGRmIDwtIGFzLmRhdGEuZnJhbWUoeCkKICBjb3JyZXMgPC0gd2lsY294LnRlc3QoZGVuc2l0eSB+IGNsdXN0ZXIsIGRmKQogIAogIHB2YWwgPC0gY29ycmVzJHAudmFsdWUKICBlcSA8LSBzdWJzdGl0dXRlKGl0YWxpYyhQKT09cCwgbGlzdChwPWZvcm1hdChwdmFsLCBkaWdpdHM9MykpKQogIHJldHVybihhcy5jaGFyYWN0ZXIoYXMuZXhwcmVzc2lvbihlcSkpKQp9KSwgYygidGlsdHlwZSIsICJwLnZhbHVlIikpCgoKZ2dwbG90KHNhbXBsZV9kZl9tZWx0ZWQsIGFlcyh4PWNsdXN0ZXIsIHk9ZGVuc2l0eSkpICsgZ2VvbV9ib3hwbG90KCkgKyB0aGVtZV9idygpICsgdGhlbWVfUHVibGljYXRpb24oKSArICBnZW9tX2ppdHRlcihhZXMoY29sb3VyPXBhdGllbnRfaWQpLCBwb3NpdGlvbiA9IHBvc2l0aW9uX2ppdHRlcih3aWR0aD0wLjIsIGhlaWdodD0wKSkgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gcGFsX3BhdGllbnQpICsgZmFjZXRfd3JhcCh+IHRpbHR5cGUsIHNjYWxlcyA9ICJmcmVlIikgKyAKICBnZW9tX3RleHQoZGF0YT1wdmFscywgYWVzKHg9SW5mLCB5PUluZiwgbGFiZWw9cC52YWx1ZSksIGhqdXN0PTEuMSwgdmp1c3Q9MS41LHNpemU9MyxwYXJzZT1UUlVFKQpgYGAKCgpgYGB7cn0KZGl2ZXJzaXR5X2NvbHVtbiA8LSAib2JzZXJ2ZWREaXZlcnNpdHlfbWVhbiIKCmRpdmVyc2l0eV9maWxlcyA8LSBsaXN0KHRjcj10Y3JfZGl2ZXJzaXR5X2ZpbGUsIGJjcj1iY3JfZGl2ZXJzaXR5X2ZpbGUpCgpkaXZlcnNpdHkgPC0gbGFwcGx5KG5hbWVzKGRpdmVyc2l0eV9maWxlcyksIGZ1bmN0aW9uKHNlZ21lbnQpIHsKICBmIDwtIGRpdmVyc2l0eV9maWxlc1tbc2VnbWVudF1dCiAgeGNyX2RpdmVyc2l0eSA8LSByZWFkX3hjcl9kaXZlcnNpdHlfZmlsZShmLCBkYl9wYXRoKQogIAogIGRmIDwtIHN1YnNldCh4Y3JfZGl2ZXJzaXR5LCBzZWxlY3Q9YygiY29uZGVuc2VkX2lkIiwgInBhdGllbnRfaWQiLCBkaXZlcnNpdHlfY29sdW1uKSkKICBjb2xuYW1lcyhkZikgPC0gbWFwdmFsdWVzKGNvbG5hbWVzKGRmKSwgZnJvbT1kaXZlcnNpdHlfY29sdW1uLCB0bz1zZWdtZW50KQogIHJldHVybihkZikKfSkKbmFtZXMoZGl2ZXJzaXR5KSA8LSBuYW1lcyhkaXZlcnNpdHlfZmlsZXMpCgp4Y3JfZGl2ZXJzaXR5IDwtIFJlZHVjZShwbHlyOjpqb2luLCBkaXZlcnNpdHkpCgpYQ1JfVkFSUyA8LSBjKCJ0Y3IiLCAiYmNyIikKYGBgCgoKYGBge3J9Cm1vbHN1YnR5cGVzIDwtIGZyZWFkKG1vbGVjdWxhcl9zdWJ0eXBlX2ZpbGUpCm1vbHN1YnR5cGVzIDwtIHNldE5hbWVzKG1vbHN1YnR5cGVzLCBjKCJjb25kZW5zZWRfaWQiLCAic3VidHlwZSIpKQoKcHJvcG9ydGlvbl9zdWJjbG9uYWxpdHkgPC0gZnJlYWQocHJvcG9ydGlvbl9zdWJjbG9uYWxpdHlfZmlsZSkKcHJvcG9ydGlvbl9zdWJjbG9uYWxpdHlfc3Vic2V0IDwtIHN1YnNldChwcm9wb3J0aW9uX3N1YmNsb25hbGl0eSwgc2VsZWN0PWMoImNvbmRlbnNlZF9pZCIsICJwcm9wb3J0aW9uX3N1YmNsb25hbCIpKQoKZXhwcnMgPC0gZnJlYWQobmFub3N0cmluZ19kYXRhX3BhdGgpCmxhYmVscyA8LSBmcmVhZChuYW5vc3RyaW5nX2Fubm90YXRpb25zX3BhdGgpCgpjZWxsdHlwZV9tYXRyaXggPC0gY3JlYXRlX2NlbGx0eXBlX21hdHJpeChleHBycywgbGFiZWxzLCBkYl9wYXRoKQpwYXRod2F5X21hdHJpeCA8LSBjcmVhdGVfcGF0aHdheV9tYXRyaXgoZXhwcnMsIGxhYmVscywgZGJfcGF0aCkKCmNlbGx0eXBlX2RmIDwtIHJvd25hbWVzX3RvX2NvbHVtbihhcy5kYXRhLmZyYW1lKHQoY2VsbHR5cGVfbWF0cml4KSksIHZhciA9ICJjb25kZW5zZWRfaWQiKQpwYXRod2F5X2RmIDwtIHJvd25hbWVzX3RvX2NvbHVtbihhcy5kYXRhLmZyYW1lKHQocGF0aHdheV9tYXRyaXgpKSwgdmFyID0gImNvbmRlbnNlZF9pZCIpCgpOQU5PU1RSSU5HX1ZBUlMgPC0gYyhyb3duYW1lcyhjZWxsdHlwZV9tYXRyaXgpLCByb3duYW1lcyhwYXRod2F5X21hdHJpeCkpCmBgYAoKCgpgYGB7cn0KY29sbmFtZXMoc2FtcGxlX2NsdXN0cykgPC0gbWFwdmFsdWVzKGNvbG5hbWVzKHNhbXBsZV9jbHVzdHMpLCAibmV3X2lkIiwgImNvbmRlbnNlZF9pZCIpCgppaGNfc3RhYmlsaXplX3N1YnNldCA8LSBzdGFiaWxpemVfaWhjX3ZhcmlhbmNlcyhpaGNfdGFibGVfc2xpZGUsIGloY190YWJsZSwgdGlsdHlwZXMpCgpkYXRhX21hdHJpY2VzIDwtIGxpc3Qoc2FtcGxlX2NsdXN0cywgaWhjX3N0YWJpbGl6ZV9zdWJzZXQsIHhjcl9kaXZlcnNpdHksIGNlbGx0eXBlX2RmLCBwYXRod2F5X2RmLCBwcm9wb3J0aW9uX3N1YmNsb25hbGl0eV9zdWJzZXQpCmRhdGFfbWF0cmljZXMgPC0gbGFwcGx5KGRhdGFfbWF0cmljZXMsIGZ1bmN0aW9uKHgpIHsKICB4ICU+JSBkcGx5cjo6c2VsZWN0KC1vbmVfb2YoInBhdGllbnRfaWQiKSkKfSkKCmNvbWJpbmVkX2RmIDwtIFJlZHVjZShmdW5jdGlvbih4LCB5KSBwbHlyOjpqb2luKHgseSx0eXBlPSdmdWxsJyksIGRhdGFfbWF0cmljZXMpCmNvbWJpbmVkX2RmIDwtIHN1YnNldChjb21iaW5lZF9kZiwgIWlzLm5hKGNsdXN0ZXIpICYgIWNvbmRlbnNlZF9pZCAlaW4lIGMoIjdfQnJuTSIpKQoKY29tYmluZWRfbWF0IDwtIHN1YnNldChjb21iaW5lZF9kZiwgc2VsZWN0PS1jKGNvbmRlbnNlZF9pZCwgY2x1c3RlcikpCnJvd25hbWVzKGNvbWJpbmVkX21hdCkgPC0gY29tYmluZWRfZGYkY29uZGVuc2VkX2lkCmBgYAoKYGBge3IsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTh9CiNyZWZfZGVuZHJvZ3JhbSA8LSBwcnVuZShhcy5kZW5kcm9ncmFtKHNhbXBsZV9oZWF0JHRyZWVfcm93KSwgIjdfQnJuTSIpCgpzYW1wbGVfb3JkZXIgPC0gc2FtcGxlX2hlYXQkdHJlZV9yb3ckbGFiZWxzW3NhbXBsZV9oZWF0JHRyZWVfcm93JG9yZGVyXQpzYW1wbGVfb3JkZXIgPC0gaW50ZXJzZWN0KHNhbXBsZV9vcmRlciwgcm93bmFtZXMoY29tYmluZWRfbWF0KSkKCmNsdXN0ZXJfYW5ub3RhdGlvbnMgPC0gc3Vic2V0KGNvbWJpbmVkX2RmLCBzZWxlY3Q9Yyhjb25kZW5zZWRfaWQsIGNsdXN0ZXIpKQoKU0lHUyA8LSBjKCJTVi0zIiwgIlNOVi01IiwgIlNWLTEiLCAiU1YtMiIsICJTTlYtMSIpCnNpZ19hbm5vdGF0aW9ucyA8LSByb3duYW1lc190b19jb2x1bW4oZGF0YS5mcmFtZShwcm9wc19zYW1wbGVfbWF0WyxTSUdTXSksIHZhciA9ICJjb25kZW5zZWRfaWQiKQoKIyMgVGFrZSBsb2dhcml0aG1zIHRvIH52YXJpYW5jZSBzdGFiaWxpemUKdmFyaWFudF9jb3VudF9hbm5vdGF0aW9ucyA8LSB2YXJpYW50X3NhbXBsZSAlPiUgZ3JvdXBfYnlfKC5kb3RzPSJjb25kZW5zZWRfaWQiKSAlPiUgCiAgc3VtbWFyaXNlKHNudl9jb3VudD1sb2coc3VtKHNudl9jb3VudCkpLCBia3B0X2NvdW50PWxvZyhzdW0oYmtwdF9jb3VudCkpKSAlPiUKICBzdWJzZXQoc2VsZWN0PWMoImNvbmRlbnNlZF9pZCIsICJzbnZfY291bnQiLCAiYmtwdF9jb3VudCIpKQoKcm93X2Fubm90YXRpb25zIDwtIFJlZHVjZShwbHlyOjpqb2luLCBsaXN0KGNsdXN0ZXJfYW5ub3RhdGlvbnMsIG1vbHN1YnR5cGVzLCBzaWdfYW5ub3RhdGlvbnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXJpYW50X2NvdW50X2Fubm90YXRpb25zKSkKcm93X2Fubm90YXRpb25zIDwtIHJvd19hbm5vdGF0aW9ucyAlPiUgY29sdW1uX3RvX3Jvd25hbWVzKHZhciA9ICJjb25kZW5zZWRfaWQiKQoKY29tYmluZWRfbWF0X3NjYWxlZCA8LSBzY2FsZShjb21iaW5lZF9tYXQpCgojIGhjcyA8LSBsYXBwbHkodW5pcXVlKGNvbWJpbmVkX2RmJGNsdXN0ZXIpLCBmdW5jdGlvbihjbHVzdCkgewojICAgaWRzIDwtIHN1YnNldChjb21iaW5lZF9kZiwgY2x1c3RlciA9PSBjbHVzdCkkY29uZGVuc2VkX2lkCiMgICBkaXN0cyA8LSBkaXN0KGNvbWJpbmVkX21hdF9zY2FsZWRbaWRzLF0sIG1ldGhvZCA9ICJldWNsaWRlYW4iKQojICAgI2Rpc3RzIDwtIHByb3h5OjpkaXN0KGNvbWJpbmVkX21hdF9zY2FsZWRbaWRzLF0sIG1ldGhvZCA9IGZ1bmN0aW9uKHgseSkgcGFpcndpc2VfZGlzdCh4LHksbWV0aG9kPSJjYW5iZXJyYSIpKQojICAgaGNsdXN0KGRpc3RzLCBtZXRob2QgPSAid2FyZC5EIikKIyB9KQojIG1pbl9oZWlnaHQgPC0gbWF4KHNhcHBseShoY3MsIGZ1bmN0aW9uKHgpIG1heCh1bmlxdWUoY29waGVuZXRpYyh4KSkpKSkqMS4xCiMgCiMgaGMgPC0gUmVkdWNlKGZ1bmN0aW9uKHgseSkgbWVyZ2UoYXMuZGVuZHJvZ3JhbSh4KSxhcy5kZW5kcm9ncmFtKHkpLGhlaWdodCA9IG1pbl9oZWlnaHQpLCBoY3MpCiMgaGMgPC0gYXMuZGVuZHJvZ3JhbShyZWZfZGVuZHJvZ3JhbSkKIyBoYyA8LSBhcy5kZW5kcm9ncmFtKHNhbXBsZV9oZWF0MiR0cmVlX3JvdykKIyAKIyBoYSA8LSBIZWF0bWFwQW5ub3RhdGlvbihyb3dfYW5ub3RhdGlvbnNbcm93bmFtZXMoY29tYmluZWRfbWF0X3NjYWxlZCksXSwgd2hpY2g9InJvdyIpCiMgCiMgSGVhdG1hcChjbGlwX3ZhbHVlcyhjb21iaW5lZF9tYXRfc2NhbGVkLCAyLCAtMiksIGNsdXN0ZXJfcm93cyA9IGhjMiwgY2x1c3Rlcl9jb2x1bW5zID0gVFJVRSwgc3BsaXQgPSAyLCBjbHVzdGVyaW5nX21ldGhvZF9jb2x1bW5zID0gIndhcmQuRDIiKSArIGhhCgpwaGVhdG1hcChjbGlwX3ZhbHVlcyhjb21iaW5lZF9tYXRfc2NhbGVkLCAyLCAtMilbc2FtcGxlX29yZGVyLF0sIGNsdXN0ZXJfcm93cyA9IEZBTFNFLCBjbHVzdGVyX2NvbHMgPSBUUlVFLCBhbm5vdGF0aW9uX3JvdyA9IHJvd19hbm5vdGF0aW9ucywgZm9udHNpemUgPSA2KQpgYGAKCldoYXQgd2UgY2FuIHNlZSBpczoKCiogSC1IUkQgY2x1c3RlciBpcyByZWxhdGl2ZWx5IGhvbW9nZW5lb3VzLgoqIExvb2tzIGxpa2UgdGhlcmUgYXJlIHR3byBjbHVzdGVycyB3aXRoaW4gdGhlIEgtRkJJIGdyb3VwIC0tIG9uZSBjaGFyYWN0ZXJpemVkIGJ5IGhpZ2ggbGV2ZWxzIG9mIGltbXVuZSBhY3Rpdml0eSAoY3l0b3RveGljaXR5LCBldGMuKSBhbmQgb25lIGNoYXJhY3Rlcml6ZWQgYnkgbG93IGxldmVscy4gCgpUaGlzIGlzIGEgcHJldHR5IHNpZ25pZmljYW50IGZpbmRpbmcuIAoKQWRkaXRpb25hbGx5LCBhbiBpbnRlcmVzdGluZyB0aGluZyBpcyB0aGF0IHRoZSBwYXRpZW50cy9zYW1wbGVzIHdpdGggdGhlIGhpZ2hlc3QgcHJvcG9ydGlvbnMgb2YgZm9sZGJhY2tzIC0tIHBhdGllbnRzIDIsIDMsIGFuZCA5IC0tIGFyZSBhY3R1YWxseSB0aGUgb25lcyB3aXRoIGhpZ2ggaW1tdW5lIHJlc3BvbnNlIGluIHRoZSBmb2xkYmFjayBncm91cCEgU3VnZ2VzdGl2ZSB0aGF0IHBlcmhhcHMgZm9sZGJhY2sgaW52ZXJzaW9ucyBjYW4gY3JlYXRlIG5lb2VwaXRvcGVzLiBPZiBjb3Vyc2UgdmVyeSBwcmVsaW1pbmFyeSBhbmQgbG93IHNhbXBsZSBzaXplIHRob3VnaC4gCgpJZiB5b3UncmUgd29uZGVyaW5nIHdoeSB0aGVyZSdzIG5vIGRlbmRyb2dyYW0gb24gdGhlIHZlcnRpY2FsIGF4aXMgaXQncyBiZWNhdXNlIHRoZSBwbG90dGluZyBmdW5jdGlvbnMgSSdtIGN1cnJlbnRseSB1c2luZyBkb24ndCBhbGxvdyBmb3Igc2VsZi1zcGVjaWZpZWQgZGVuZHJvZ3JhbXMuIFRyeWluZyB0byBtYWtlIG9uZSB0aGF0IGxldHMgbWUgZG8gc28gYnV0IGl0J3MgdGFraW5nIGEgYml0IG9mIGFjcm9iYXRpY3MgYW5kIEkndmUgd2FzdGVkIGEgbG90IG9mIHRpbWUgYWxyZWFkeSAuLi4gCgpUbyBzZWUgSUNHQyB2YWxpZGF0aW9uLCBnbyB0byB0aGF0IHNlY3Rpb24uIEknbGwgYWRkIGEgbGluayBsYXRlciAuLi4KCiMjIyMgVENSL0JDUiBkaXZlcnNpdHkKCmBgYHtyfQpjb21iaW5lZF9kZiRwYXRpZW50X2lkIDwtIG1hcF9pZChjb21iaW5lZF9kZiRjb25kZW5zZWRfaWQsIGZyb20gPSAiY29uZGVuc2VkX2lkIiwgdG89InBhdGllbnRfaWQiLCBkYl9wYXRoKQpjb21iaW5lZF9kZl94Y3IgPC0gbWVsdChjb21iaW5lZF9kZiwgaWQudmFycyA9IGMoImNvbmRlbnNlZF9pZCIsICJwYXRpZW50X2lkIiwgImNsdXN0ZXIiKSwgbWVhc3VyZS52YXJzID0gWENSX1ZBUlMsIHZhcmlhYmxlLm5hbWUgPSAidHlwZSIsIHZhbHVlLm5hbWUgPSAidmFsdWUiKQoKcHZhbHMgPC0gc2V0TmFtZXMoZGRwbHkoY29tYmluZWRfZGZfeGNyLCAuKHR5cGUpLCBmdW5jdGlvbih4KSB7CiAgZGYgPC0gYXMuZGF0YS5mcmFtZSh4KQogIGNvcnJlcyA8LSB3aWxjb3gudGVzdCh2YWx1ZSB+IGNsdXN0ZXIsIGRmKQogIAogIHB2YWwgPC0gY29ycmVzJHAudmFsdWUKICBlcSA8LSBzdWJzdGl0dXRlKGl0YWxpYyhQKT09cCwgbGlzdChwPWZvcm1hdChwdmFsLCBkaWdpdHM9MykpKQogIHJldHVybihhcy5jaGFyYWN0ZXIoYXMuZXhwcmVzc2lvbihlcSkpKQp9KSwgYygidHlwZSIsICJwLnZhbHVlIikpCgoKZ2dwbG90KGNvbWJpbmVkX2RmX3hjciwgYWVzKHg9Y2x1c3RlciwgeT12YWx1ZSkpICsgZ2VvbV9ib3hwbG90KCkgKyB0aGVtZV9idygpICsgdGhlbWVfUHVibGljYXRpb24oKSArICBnZW9tX2ppdHRlcihhZXMoY29sb3VyPXBhdGllbnRfaWQpLCBwb3NpdGlvbiA9IHBvc2l0aW9uX2ppdHRlcih3aWR0aD0wLjIsIGhlaWdodD0wKSkgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gcGFsX3BhdGllbnQpICsgZmFjZXRfd3JhcCh+IHR5cGUsIHNjYWxlcyA9ICJmcmVlIikgKyAKICBnZW9tX3RleHQoZGF0YT1wdmFscywgYWVzKHg9SW5mLCB5PUluZiwgbGFiZWw9cC52YWx1ZSksIGhqdXN0PTEuMSwgdmp1c3Q9MS41LHNpemU9MyxwYXJzZT1UUlVFKQpgYGAKCiMjIyMgQ2VsbHR5cGVzIGFuZCBwYXRod2F5cwoKYGBge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD0xMH0KY29tYmluZWRfZGYkcGF0aWVudF9pZCA8LSBtYXBfaWQoY29tYmluZWRfZGYkY29uZGVuc2VkX2lkLCBmcm9tID0gImNvbmRlbnNlZF9pZCIsIHRvPSJwYXRpZW50X2lkIiwgZGJfcGF0aCkKY29tYmluZWRfZGZfbmFub3N0cmluZyA8LSBtZWx0KGNvbWJpbmVkX2RmLCBpZC52YXJzID0gYygiY29uZGVuc2VkX2lkIiwgInBhdGllbnRfaWQiLCAiY2x1c3RlciIpLCBtZWFzdXJlLnZhcnMgPSBOQU5PU1RSSU5HX1ZBUlMsIHZhcmlhYmxlLm5hbWUgPSAidHlwZSIsIHZhbHVlLm5hbWUgPSAidmFsdWUiKQoKcHZhbHMgPC0gc2V0TmFtZXMoZGRwbHkoY29tYmluZWRfZGZfbmFub3N0cmluZywgLih0eXBlKSwgZnVuY3Rpb24oeCkgewogIGRmIDwtIGFzLmRhdGEuZnJhbWUoeCkKICBjb3JyZXMgPC0gd2lsY294LnRlc3QodmFsdWUgfiBjbHVzdGVyLCBkZikKICAKICBwdmFsIDwtIGNvcnJlcyRwLnZhbHVlCiAgZXEgPC0gc3Vic3RpdHV0ZShpdGFsaWMoUCk9PXAsIGxpc3QocD1mb3JtYXQocHZhbCwgZGlnaXRzPTMpKSkKICByZXR1cm4oYXMuY2hhcmFjdGVyKGFzLmV4cHJlc3Npb24oZXEpKSkKfSksIGMoInR5cGUiLCAicC52YWx1ZSIpKQoKCmdncGxvdChjb21iaW5lZF9kZl9uYW5vc3RyaW5nLCBhZXMoeD1jbHVzdGVyLCB5PXZhbHVlKSkgKyBnZW9tX2JveHBsb3QoKSArIHRoZW1lX2J3KCkgKyB0aGVtZV9QdWJsaWNhdGlvbigpICsgIGdlb21faml0dGVyKGFlcyhjb2xvdXI9cGF0aWVudF9pZCksIHBvc2l0aW9uID0gcG9zaXRpb25faml0dGVyKHdpZHRoPTAuMiwgaGVpZ2h0PTApKSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBwYWxfcGF0aWVudCkgKyBmYWNldF93cmFwKH4gdHlwZSwgc2NhbGVzID0gImZyZWUiKSArIAogIGdlb21fdGV4dChkYXRhPXB2YWxzLCBhZXMoeD1JbmYsIHk9SW5mLCBsYWJlbD1wLnZhbHVlKSwgaGp1c3Q9MS4xLCB2anVzdD0xLjUsc2l6ZT0zLHBhcnNlPVRSVUUpCmBgYAoKIyMjIEFuY2VzdHJhbC1kZXNjZW5kYW50IGxldmVsCgpgYGB7cn0KY2x1c3RlcnMgPC0gY3V0cmVlKHBhdGllbnRfaGVhdCR0cmVlX3JvdywgTkNMVVNUKQpwYXRpZW50X2NsdXN0cyA8LSBtYWtlX2NsdXN0ZXJfZnJhbWUoY2x1c3RlcnMpCgpwcm9wc19hZF9tZXJnZWRfY2x1c3RzIDwtIGpvaW4ocHJvcHNfYWRfbWVyZ2VkLCBwYXRpZW50X2NsdXN0cykKcGF0aWVudF9kZiA8LSB1bmlxdWUoc3Vic2V0KHByb3BzX2FkX21lcmdlZF9jbHVzdHMsIHNlbGVjdD1zZWxlY3RlZF9jb2xzKSkKCnBhdGllbnRfZGZfbWVsdGVkIDwtIG1lbHQocGF0aWVudF9kZiwgaWQudmFycyA9IGNvbG5hbWVzKHBhdGllbnRfZGYpWyFjb2xuYW1lcyhwYXRpZW50X2RmKSAlaW4lIHRpbHR5cGVzXSwgbWVhc3VyZS52YXJzID0gdGlsdHlwZXMsIHZhcmlhYmxlLm5hbWUgPSAidGlsdHlwZSIsIHZhbHVlLm5hbWUgPSAiZGVuc2l0eSIpCmBgYAoKYGBge3J9CnB2YWxzIDwtIHNldE5hbWVzKGRkcGx5KHBhdGllbnRfZGZfbWVsdGVkLCAuKHRpbHR5cGUpLCBmdW5jdGlvbih4KSB7CiAgZGYgPC0gYXMuZGF0YS5mcmFtZSh4KQogIGNvcnJlcyA8LSB3aWxjb3gudGVzdChkZW5zaXR5IH4gY2x1c3RlciwgZGYpCiAgCiAgcHZhbCA8LSBjb3JyZXMkcC52YWx1ZQogIGVxIDwtIHN1YnN0aXR1dGUoaXRhbGljKFApPT1wLCBsaXN0KHA9Zm9ybWF0KHB2YWwsIGRpZ2l0cz0zKSkpCiAgcmV0dXJuKGFzLmNoYXJhY3Rlcihhcy5leHByZXNzaW9uKGVxKSkpCn0pLCBjKCJ0aWx0eXBlIiwgInAudmFsdWUiKSkKCgpnZ3Bsb3QocGF0aWVudF9kZl9tZWx0ZWQsIGFlcyh4PWNsdXN0ZXIsIHk9ZGVuc2l0eSkpICsgZ2VvbV9ib3hwbG90KCkgKyB0aGVtZV9idygpICsgdGhlbWVfUHVibGljYXRpb24oKSArICBnZW9tX2ppdHRlcihhZXMoY29sb3VyPXBhdGllbnRfaWQpLCBwb3NpdGlvbiA9IHBvc2l0aW9uX2ppdHRlcih3aWR0aD0wLjIsIGhlaWdodD0wKSkgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gcGFsX3BhdGllbnQpICsgZmFjZXRfd3JhcCh+IHRpbHR5cGUsIHNjYWxlcyA9ICJmcmVlIikgKyAKICBnZW9tX3RleHQoZGF0YT1wdmFscywgYWVzKHg9SW5mLCB5PUluZiwgbGFiZWw9cC52YWx1ZSksIGhqdXN0PTEuMSwgdmp1c3Q9MS41LHNpemU9MyxwYXJzZT1UUlVFKQpgYGAKCgpIZW5jZSwgdGhlIGZvbGRiYWNrIGdyb3VwIChjbHVzdGVyIDIpIGhhcyBsb3dlciBDRDgrIGFuZCBDRDQrIGRlbnNpdGllcyB0aGFuIHRoZSBIUkQgZ3JvdXAsIGFzIGV4cGVjdGVkLiAKCkEgY2F2ZWF0IGlzIHRoYXQgcC12YWx1ZXMgYXJlIGNvbXB1dGVkIHdpdGggV2lsY294b24sIGlycmVzcGVjdGl2ZSBvZiBwYXRpZW50IG51bWJlci4gV2UgY2FuJ3QgcmVhbGx5IG9wdCBmb3IgYSBub25wYXJhbWV0cmljIG5lc3RlZCByYW5rcyB0ZXN0IGJlY2F1c2UgdmVyeSBmZXcgcGF0aWVudHMgKDQpIGFyZSBhY3R1YWxseSBwcmVzZW50IGluIGJvdGggY2x1c3RlcnMuIAoKIyMgQW5jZXN0cmFsIGFuYWx5c2lzCgpBbmNlc3RyYWwgdmFyaWFudHMgbWF5IGhhdmUgZGlmZmVyZW50IHByb3BlcnRpZXMgZnJvbSBkZXNjZW5kYW50IHZhcmlhbnRzLiAKCiMjIyBTYW1wbGUtc3BlY2lmaWMKCkhlcmUgd2UnbGwganVzdCBuYWl2ZWx5IGFsbG93IGZvciBzdWJjbG9uYWwgdmFyaWFudHMgdG8gYmUgY291bnRlZCBtdWx0aXBsZSB0aW1lcy4gCgpgYGB7cn0KcHJvcHNfYWRfbWVyZ2VkJGlzX2FuY2VzdHJhbCA8LSBhcy5mYWN0b3IocHJvcHNfYWRfbWVyZ2VkJGlzX2FuY2VzdHJhbCkKCnB2YWxzIDwtIHNldE5hbWVzKGRkcGx5KHByb3BzX2FkX21lcmdlZCwgLihzaWduYXR1cmUpLCBmdW5jdGlvbih4KSB7CiAgZGYgPC0gYXMuZGF0YS5mcmFtZSh4KQogIGNvcnJlcyA8LSB3aWxjb3gudGVzdChwcm9wb3J0aW9uIH4gaXNfYW5jZXN0cmFsLCBkZikKICAKICBwdmFsIDwtIGNvcnJlcyRwLnZhbHVlCiAgZXEgPC0gc3Vic3RpdHV0ZShpdGFsaWMoUCk9PXAsIGxpc3QocD1mb3JtYXQocHZhbCwgZGlnaXRzPTMpKSkKICByZXR1cm4oYXMuY2hhcmFjdGVyKGFzLmV4cHJlc3Npb24oZXEpKSkKfSksIGMoInNpZ25hdHVyZSIsICJwLnZhbHVlIikpCgpnZ3Bsb3QocHJvcHNfYWRfbWVyZ2VkLCBhZXMoeD1pc19hbmNlc3RyYWwsIHk9cHJvcG9ydGlvbikpICsgZ2VvbV9ib3hwbG90KCkgKyBnZW9tX2ppdHRlcihhZXMoY29sb3VyPXBhdGllbnRfaWQpLCBwb3NpdGlvbj1wb3NpdGlvbl9qaXR0ZXIod2lkdGg9MC4yLCBoZWlnaHQ9MCkpICsgdGhlbWVfYncoKSArIHRoZW1lX1B1YmxpY2F0aW9uKCkgKyBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IHBhbF9wYXRpZW50KSArIGZhY2V0X3dyYXAofiBzaWduYXR1cmUsIHNjYWxlcz0gImZyZWUiKSArIAogIGdlb21fdGV4dChkYXRhPXB2YWxzLCBhZXMoeD1JbmYsIHk9SW5mLCBsYWJlbD1wLnZhbHVlKSwgaGp1c3Q9MS4xLCB2anVzdD0xLjUsc2l6ZT0zLHBhcnNlPVRSVUUpCmBgYAoKUC12YWx1ZXMgYXJlIHVuY29ycmVjdGVkLiAKClVuc3VycHJpc2luZ2x5LCBhbmNlc3RyYWwgc2FtcGxlcyBoYXZlIHNpZ25pZmljYW50bHkgbW9yZSBvZiBTTlYtNCwgdGhlIGFnZSBzaWduYXR1cmUuIEluc2lnbmlmaWNhbnQsIGJ1dCB0aGV5IGFsc28gaGF2ZSBsZXNzIFNWLTEsIHdoaWNoIGlzIG9uZSBvZiB0aGUgQlJDQSdzIChzbWFsbCBkZWxldGlvbnMpLiAKCiMjIyBVbmlvbgoKSGVyZSB3ZSdsbCBhY3R1YWxseSB0YWtlIHRoZSB1bmlvbiBvZiBzdWJjbG9uYWwgdmFyaWFudHMgZm9yIGVhY2ggcGF0aWVudCBzbyB3ZSBkb24ndCBvdmVyY291bnQuIAoKYGBge3J9CnByb3BzX3BhdGllbnRfYWRfbWVyZ2VkJGlzX2FuY2VzdHJhbCA8LSBhcy5mYWN0b3IocHJvcHNfcGF0aWVudF9hZF9tZXJnZWQkaXNfYW5jZXN0cmFsKQoKcHZhbHMgPC0gc2V0TmFtZXMoZGRwbHkocHJvcHNfcGF0aWVudF9hZF9tZXJnZWQsIC4oc2lnbmF0dXJlKSwgZnVuY3Rpb24oeCkgewogIGRmIDwtIGFzLmRhdGEuZnJhbWUoeCkKICBjb3JyZXMgPC0gd2lsY294LnRlc3QocHJvcG9ydGlvbiB+IGlzX2FuY2VzdHJhbCwgZGYsIHBhaXJlZD1UUlVFKQogIAogIHB2YWwgPC0gY29ycmVzJHAudmFsdWUKICBlcSA8LSBzdWJzdGl0dXRlKGl0YWxpYyhQKT09cCwgbGlzdChwPWZvcm1hdChwdmFsLCBkaWdpdHM9MykpKQogIHJldHVybihhcy5jaGFyYWN0ZXIoYXMuZXhwcmVzc2lvbihlcSkpKQp9KSwgYygic2lnbmF0dXJlIiwgInAudmFsdWUiKSkKCmdncGxvdChwcm9wc19wYXRpZW50X2FkX21lcmdlZCwgYWVzKHg9aXNfYW5jZXN0cmFsLCB5PXByb3BvcnRpb24pKSArIGdlb21fYm94cGxvdCgpICsgZ2VvbV9qaXR0ZXIoYWVzKGNvbG91cj1wYXRpZW50X2lkKSwgcG9zaXRpb249cG9zaXRpb25faml0dGVyKHdpZHRoPTAuMiwgaGVpZ2h0PTApKSArIHRoZW1lX2J3KCkgKyB0aGVtZV9QdWJsaWNhdGlvbigpICsgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBwYWxfcGF0aWVudCkgKyBmYWNldF93cmFwKH4gc2lnbmF0dXJlLCBzY2FsZXM9ICJmcmVlIikgKyAKICBnZW9tX3RleHQoZGF0YT1wdmFscywgYWVzKHg9SW5mLCB5PUluZiwgbGFiZWw9cC52YWx1ZSksIGhqdXN0PTEuMSwgdmp1c3Q9MS41LHNpemU9MyxwYXJzZT1UUlVFKQpgYGAKCkFzaWRlIGZyb20gYWdlIGFuZCBIUkQgd2hpY2ggd2VyZSBkZXNjcmliZWQgaW4gTWNQaGVyc29uIGV0IGFsLiwgdGhlcmUncyBhZGRpdGlvbmFsbHkgdGhlIFNWLTMgc2lnbmF0dXJlIC0tIGEgdHJhbnNsb2NhdGlvbiBzaWduYXR1cmUuIEltcGx5aW5nIHRoYXQgdHJhbnNsb2NhdGlvbnMgYXJlIGVhcmx5IChhbmNlc3RyYWwpIGV2ZW50cyBpbiBIR1NDIC0tIHBlcmhhcHMgdGhpcyBpcyBpbnRlcmVzdGluZz8gSSBoYXZlIHRvIGRvIGEgbGl0ZXJhdHVyZSBzZWFyY2guIAoKCmBgYHtyfQphbmNfZGVzY19wYXRpZW50IDwtIGRjYXN0KHByb3BzX3BhdGllbnRfYWRfbWVyZ2VkLCBmb3JtdWxhID0gcGF0aWVudF9pZCArIHNpZ25hdHVyZSB+IGlzX2FuY2VzdHJhbCwgdmFsdWUudmFyID0gInByb3BvcnRpb24iKQoKcHZhbHMgPC0gc2V0TmFtZXMoZGRwbHkoYW5jX2Rlc2NfcGF0aWVudCwgLihzaWduYXR1cmUpLCBmdW5jdGlvbih4KSB7CiAgZGYgPC0gYXMuZGF0YS5mcmFtZSh4KQogIGNvcnJlcyA8LSB3aXRoKGRmLCBjb3IudGVzdChgMGAsIGAxYCwgbWV0aG9kPSJzcGVhcm1hbiIpKQogIAogIHB2YWwgPC0gY29ycmVzJHAudmFsdWUKICBlcSA8LSBzdWJzdGl0dXRlKGl0YWxpYyhQKT09cCwgbGlzdChwPWZvcm1hdChwdmFsLCBkaWdpdHM9MykpKQogIHJldHVybihhcy5jaGFyYWN0ZXIoYXMuZXhwcmVzc2lvbihlcSkpKQp9KSwgYygic2lnbmF0dXJlIiwgInAudmFsdWUiKSkKCmdncGxvdChhbmNfZGVzY19wYXRpZW50LCBhZXMoeD1gMWAsIHk9YDBgKSkgKyBnZW9tX3BvaW50KCkgKyBnZW9tX3BvaW50KGFlcyhjb2xvdXI9cGF0aWVudF9pZCkpICsgdGhlbWVfYncoKSArIHRoZW1lX1B1YmxpY2F0aW9uKCkgKyBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IHBhbF9wYXRpZW50KSArIGZhY2V0X3dyYXAofiBzaWduYXR1cmUsIHNjYWxlcz0gImZyZWUiKSArIGdlb21fdGV4dChkYXRhPXB2YWxzLCBhZXMoeD1JbmYsIHk9SW5mLCBsYWJlbD1wLnZhbHVlKSwgaGp1c3Q9MS4xLCB2anVzdD0xLjUsc2l6ZT0zLHBhcnNlPVRSVUUpCmBgYAoKIyMgSUNHQyB2YWxpZGF0aW9uCgojIyMgU2lnbmF0dXJlcwoKIVtdKGByIG1tY3RtX292X2NvbWJpbmVkX3NpZ3Bsb3RgKQoKIyMjIEZCSSBzdWJjbHVzdGVycwoKYGBge3J9CnNwZWNpbWVuX2RhdGEgPC0gZnJlYWQoaWNnY19zcGVjaW1lbl9maWxlKQppY2djX3N1YnR5cGVzIDwtIGZyZWFkKGljZ2Nfc3VidHlwZV9maWxlKQppY2djX2V4cHJfbWF0IDwtIGZyZWFkKGljZ2NfZXhwcl9tYXRfZmlsZSkKaWNnY19leHByX2RmIDwtIGljZ2NfZXhwcl9tYXQgJT4lIGFzLmRhdGEuZnJhbWUgJT4lIGNvbHVtbl90b19yb3duYW1lcygiaWNnY19kb25vcl9pZCIpICU+JSB0ICU+JSBhcy5kYXRhLmZyYW1lICU+JSByb3duYW1lc190b19jb2x1bW4oIk5hbWUiKQppY2djX2NlbGx0eXBlX21hdHJpeCA8LSBjcmVhdGVfcGF0aHdheV9tYXRyaXgoaWNnY19leHByX2RmLCBsYWJlbHMsIGRiX3BhdGgsIGNvbnZlcnRfaWRzID0gRkFMU0UpCgppY2djX2ltbXVuZSA8LSByb3duYW1lc190b19jb2x1bW4oYXMuZGF0YS5mcmFtZSh0KGljZ2NfY2VsbHR5cGVfbWF0cml4KSksIHZhciA9ICJpY2djX2Rvbm9yX2lkIikKCiMgY3l0b3RveGljX21hcmtlcnMgPC0gYygiUFJGMSIsICJHWk1BIiwgIkhMQS1BIiwgIkhMQS1CIiwgIkhMQS1DIiwgIkdaTUsiLCAiR1pNTSIsICJHWk1IIiwgIkdOTFkiLCAiR1pNQiIpCiMgaWNnY19pbW11bmUgPC0gYXMuZGF0YS5mcmFtZShzdWJzZXQoaWNnY19leHByX21hdCwgc2VsZWN0PWMoImljZ2NfZG9ub3JfaWQiLCBjeXRvdG94aWNfbWFya2VycykpKQojIGZvciAoaSBpbiAyOm5jb2woaWNnY19pbW11bmUpKSB7CiMgIGljZ2NfaW1tdW5lWyxpXSA8LSBsb2cxMChpY2djX2ltbXVuZVssaV0pCiMgfQpgYGAKCmBgYHtyLCBmaWcuaGVpZ2h0PTEyfQpwcm9wc19vdl9jb21iaW5lZCA8LSByZWFkX211dHNpZ19vdXRwdXQobW1jdG1fb3ZfY29tYmluZWRfcmVzdWx0X2Rpciwgc2FtcGxlcywgZXh0ZXJuYWwgPSBUUlVFKQpwcm9wc19vdl9jb21iaW5lZF9wcm9wIDwtIHByb3BzX292X2NvbWJpbmVkJHByb3AKCnByb3BzX292X2NvbWJpbmVkX21hdCA8LSBjcmVhdGVfbXV0c2lnX21hdHJpeChwcm9wc19vdl9jb21iaW5lZF9wcm9wLCBjb2wgPSAicHJvcG9ydGlvbiIpCgojIHByb3BzX292X2NvbWJpbmVkX21hdF9zY2FsZWQgPC0gYXMuZGF0YS5mcmFtZShhcHBseShwcm9wc19vdl9jb21iaW5lZF9tYXQsIDIsIGZ1bmN0aW9uKHgpIHsKIyAgICN4IDwtIGxvZ2l0KHgpCiMgICAoeC1tZWRpYW4oeCxuYS5ybT1UUlVFKSkvbWFkKHgsbmEucm09VFJVRSkKIyB9KSkKCnByb3BzX292X2NvbWJpbmVkX21hdF9zY2FsZWQgPC0gc2NhbGUocHJvcHNfb3ZfY29tYmluZWRfbWF0KQoKI3Byb3BzX292X2NvbWJpbmVkX21hdF9zY2FsZWQgPC0gY2xpcF92YWx1ZXMocHJvcHNfb3ZfY29tYmluZWRfbWF0X3NjYWxlZCwgMywgLTMpCiNjbGlwX3ZhbHVlcyhzY2FsZShwcm9wc19vdl9jb21iaW5lZF9tYXQpLCAyLCAtMikKCm92X2NvbWJpbmVkX2hlYXQgPC0gcGhlYXRtYXAocHJvcHNfb3ZfY29tYmluZWRfbWF0X3NjYWxlZCwgZm9udHNpemVfcm93ID0gNiwgY2x1c3RlcmluZ19kaXN0YW5jZV9yb3dzID0gImNvcnJlbGF0aW9uIiwgY2x1c3RlcmluZ19tZXRob2QgPSAid2FyZC5EMiIpCmBgYAoKYGBge3J9Ck5DTFVTVCA8LSAzCgpjbHVzdGVycyA8LSBjdXRyZWUob3ZfY29tYmluZWRfaGVhdCR0cmVlX3JvdywgTkNMVVNUKQpvdl9jb21iaW5lZF9jbHVzdHMgPC0gbWFrZV9jbHVzdGVyX2ZyYW1lKGNsdXN0ZXJzKQoKY29sbmFtZXMoaWNnY19pbW11bmUpIDwtIG1hcHZhbHVlcyhjb2xuYW1lcyhpY2djX2ltbXVuZSksICJpY2djX2Rvbm9yX2lkIiwgIm5ld19pZCIpCgpkYXRhX21hdHJpY2VzIDwtIGxpc3Qob3ZfY29tYmluZWRfY2x1c3RzLCBpY2djX2ltbXVuZSkKZGF0YV9tYXRyaWNlcyA8LSBsYXBwbHkoZGF0YV9tYXRyaWNlcywgZnVuY3Rpb24oeCkgewogIHggJT4lIGRwbHlyOjpzZWxlY3QoLW9uZV9vZigicGF0aWVudF9pZCIpKQp9KQoKY29tYmluZWRfZGYgPC0gUmVkdWNlKGZ1bmN0aW9uKHgsIHkpIHBseXI6OmpvaW4oeCx5LHR5cGU9J2lubmVyJyksIGRhdGFfbWF0cmljZXMpCiNjb21iaW5lZF9kZiA8LSBzdWJzZXQoY29tYmluZWRfZGYsIGNsdXN0ZXIgPT0gMSkKCmNvbWJpbmVkX21hdCA8LSBzdWJzZXQoY29tYmluZWRfZGYsIHNlbGVjdD0tYyhuZXdfaWQsIGNsdXN0ZXIpKQpyb3duYW1lcyhjb21iaW5lZF9tYXQpIDwtIGNvbWJpbmVkX2RmJG5ld19pZApgYGAKCmBgYHtyLCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD04fQpzYW1wbGVfb3JkZXIgPC0gb3ZfY29tYmluZWRfaGVhdCR0cmVlX3JvdyRsYWJlbHNbb3ZfY29tYmluZWRfaGVhdCR0cmVlX3JvdyRvcmRlcl0Kc2FtcGxlX29yZGVyIDwtIGludGVyc2VjdChzYW1wbGVfb3JkZXIsIHJvd25hbWVzKGNvbWJpbmVkX21hdCkpCgpjbHVzdGVyX2Fubm90YXRpb25zIDwtIHN1YnNldChjb21iaW5lZF9kZiwgc2VsZWN0PWMobmV3X2lkLCBjbHVzdGVyKSkKClNJR1MgPC0gYygiU1YtOCIsICJTTlYtMSIsICJTVi00IiwgIlNWLTciLCAiU1YtNSIsICJTTlYtNyIsICJTVi0xIikKc2lnX2Fubm90YXRpb25zIDwtIHJvd25hbWVzX3RvX2NvbHVtbihkYXRhLmZyYW1lKHByb3BzX292X2NvbWJpbmVkX21hdFssU0lHU10sIGNoZWNrLm5hbWVzID0gRkFMU0UpLCB2YXIgPSAibmV3X2lkIikKCnN1YnR5cGVfYW5ub3RhdGlvbnMgPC0gc3Vic2V0KGljZ2Nfc3VidHlwZXMsIHNlbGVjdD1jKCJpY2djX2Rvbm9yX2lkIiwgInN1YnR5cGUiLCAibm1mX3N1YnR5cGUiKSkKY29sbmFtZXMoc3VidHlwZV9hbm5vdGF0aW9ucylbMV0gPC0gIm5ld19pZCIKCnJvd19hbm5vdGF0aW9ucyA8LSBSZWR1Y2UocGx5cjo6am9pbiwgbGlzdChjbHVzdGVyX2Fubm90YXRpb25zLCBzaWdfYW5ub3RhdGlvbnMsIHN1YnR5cGVfYW5ub3RhdGlvbnMpKQpyb3dfYW5ub3RhdGlvbnMgPC0gcm93X2Fubm90YXRpb25zICU+JSBjb2x1bW5fdG9fcm93bmFtZXModmFyID0gIm5ld19pZCIpCgpzZWxlY3Rfcm93cyA8LSByb3duYW1lcyhzdWJzZXQocm93X2Fubm90YXRpb25zLCBgU1YtNGAgPCAxKSkKbm90eCA8LSBzdWJzZXQoc3BlY2ltZW5fZGF0YSwgc3RyX2RldGVjdChzcGVjaW1lbl90eXBlLCAiUHJpbWFyeSIpICYgc3BlY2ltZW5fZG9ub3JfdHJlYXRtZW50X3R5cGUgPT0gIm5vIHRyZWF0bWVudCIpJGljZ2NfZG9ub3JfaWQKc2FtcGxlX29yZGVyIDwtIGludGVyc2VjdChzYW1wbGVfb3JkZXIsIHNlbGVjdF9yb3dzKQpzYW1wbGVfb3JkZXIgPC0gaW50ZXJzZWN0KHNhbXBsZV9vcmRlciwgbm90eCkKCiNvcmRlcl9mYmkgPC0gb3JkZXIocm93X2Fubm90YXRpb25zW3Jvd25hbWVzKGNvbWJpbmVkX21hdF9zY2FsZWQpLF0kYFNWLThgKQoKI2NvbWJpbmVkX21hdF9zY2FsZWQgPC0gc2NhbGUoY29tYmluZWRfbWF0KQpjb21iaW5lZF9tYXRfc2NhbGVkIDwtIGFzLmRhdGEuZnJhbWUoYXBwbHkoY29tYmluZWRfbWF0LCAyLCBmdW5jdGlvbih4KSAoeC1tZWRpYW4oeCxuYS5ybT1UUlVFKSkvbWFkKHgsbmEucm09VFJVRSkpKQoKY29tYmluZWRfbWF0X3NjYWxlZCA8LSBjb21iaW5lZF9tYXRfc2NhbGVkW3NhbXBsZV9vcmRlcixdCm90aGVyX21hdCA8LSBwcm9wc19vdl9jb21iaW5lZF9tYXRfc2NhbGVkW3NhbXBsZV9vcmRlcixdCgpwaGVhdG1hcChjbGlwX3ZhbHVlcyhjYmluZChjb21iaW5lZF9tYXRfc2NhbGVkLCBvdGhlcl9tYXQpLCAyLCAtMiksIGNsdXN0ZXJfcm93cyA9IEZBTFNFLCBjbHVzdGVyX2NvbHMgPSBUUlVFLCBhbm5vdGF0aW9uX3JvdyA9IHJvd19hbm5vdGF0aW9ucywgZm9udHNpemUgPSA2LCBjbHVzdGVyaW5nX2Rpc3RhbmNlX2NvbHMgPSAiY29ycmVsYXRpb24iLCBjbHVzdGVyaW5nX21ldGhvZCA9ICJ3YXJkLkQyIikKCiNyb3dtZWFuIDwtIHJvd01lYW5zKGNvbWJpbmVkX21hdF9zY2FsZWQpCiNhIDwtIG1lcmdlKHJvd19hbm5vdGF0aW9ucywgYXMuZGF0YS5mcmFtZShyb3dtZWFuKSwgYnk9InJvdy5uYW1lcyIpCiNnZ3Bsb3Qoc3Vic2V0KGEsIGNsdXN0ZXIgIT0gMSksIGFlcyh4PXJvd21lYW4gPiBtZWRpYW4ocm93bWVhbiksIHkgPSBgU05WLTdgKSkgKyBnZW9tX2JveHBsb3QoKSArIHRoZW1lX2J3KCkKYGBgCgpgYGB7cn0KY2x1c3RlcnMgPC0gc2V0TmFtZXMoY2x1c3Rlcl9hbm5vdGF0aW9ucywgYygiaWNnY19kb25vcl9pZCIsICJjbHVzdGVyIikpCmBgYAoKIyMjIERpZmZlcmVudGlhbCBleHByZXNzaW9uCgpgYGB7cn0KaWNnY19leHByX21lbHRlZCA8LSBmcmVhZChpY2djX2V4cHJfcmF3X2ZpbGUpCmljZ2NfZXhwcl9jYXN0ZWRfbWF0cml4IDwtIGV4cHJlc3Npb25fZGZfdG9fbWF0cml4KGljZ2NfZXhwcl9tZWx0ZWQsIHN1bW1hcml6ZV9vdmVyID0gImljZ2NfZG9ub3JfaWQiLCBtZWFzdXJlX3ZhciA9ICJyYXdfcmVhZF9jb3VudCIpCgppY2djX2NvdW50cyA8LSBpY2djX2V4cHJfY2FzdGVkX21hdHJpeCAlPiUgY29sdW1uX3RvX3Jvd25hbWVzKHZhciA9ICJpY2djX2Rvbm9yX2lkIikgCmljZ2NfY291bnRzX2ZpbHRlcmVkIDwtIGljZ2NfY291bnRzW2NsdXN0ZXJzJGljZ2NfZG9ub3JfaWQsXQppY2djX2NvdW50c19maWx0ZXJlZCA8LSBpY2djX2NvdW50c19maWx0ZXJlZCAlPiUgdCAlPiUgYXMuZGF0YS5mcmFtZQoKZGdlIDwtIERHRUxpc3QoY291bnRzID0gaWNnY19jb3VudHNfZmlsdGVyZWQpCmRnZSA8LSBjYWxjTm9ybUZhY3RvcnMoZGdlKQoKbGlicmFyeV9zaXplcyA8LSBjb2xTdW1zKGljZ2NfY291bnRzX2ZpbHRlcmVkKQpsaWJyYXJ5X3NpemVzCm1heChsaWJyYXJ5X3NpemVzKS9taW4obGlicmFyeV9zaXplcykKCmRlc2lnbiA8LSBtb2RlbC5tYXRyaXgofjAgKyBmYWN0b3IoY2x1c3RlcnMkY2x1c3RlcikpCmNvbG5hbWVzKGRlc2lnbikgPC0gcGFzdGUwKCJjbHVzdCIsIDE6TkNMVVNUKQpjb250cmFzdC5tYXRyaXggPC0gbWFrZUNvbnRyYXN0cyhjbHVzdDEtY2x1c3QzLCBjbHVzdDItKGNsdXN0MStjbHVzdDMpLzIsY2x1c3QyLWNsdXN0MSwgbGV2ZWxzPWRlc2lnbikKYGBgCgojIyMjIGxpbW1hLXRyZW5kCgpXZSBzdHJhZGRsZSBjbG9zZSB0byB0aHJlc2hvbGQgZm9yIGRvaW5nIHRoaXMgYnV0IGxldCdzIGRvIGl0IGFueXdheXMuIAoKYGBge3J9CmZpdF90cmVuZCA8LSBybmFzZXFfREVfaW5pdGlhbF9maXQoZGdlLCBkZXNpZ24sIHR5cGUgPSAibGltbWFfdHJlbmQiKQpgYGAKCmBgYHtyfQpmaXRfdHJlbmRfY29udHJhc3RzIDwtIGNvbnRyYXN0cy5maXQoZml0X3RyZW5kLCBjb250cmFzdC5tYXRyaXgpCmZpdF90cmVuZF9jb250cmFzdHMgPC0gZUJheWVzKGZpdF90cmVuZF9jb250cmFzdHMpCgpocmRfZmJpX2dlbmVzIDwtIHRvcFRhYmxlKGZpdF90cmVuZF9jb250cmFzdHMsIGNvZWYgPSAiY2x1c3QyIC0gKGNsdXN0MSArIGNsdXN0MykvMiIsIGFkanVzdC5tZXRob2QgPSAiQkgiLCBudW1iZXIgPSBJbmYsIHNvcnQ9InAiKQpmYmlfaW1tdW5lX2dlbmVzIDwtIHRvcFRhYmxlKGZpdF90cmVuZF9jb250cmFzdHMsIGNvZWYgPSAiY2x1c3QxIC0gY2x1c3QzIiwgbnVtYmVyID0gSW5mLCBzb3J0PSJwIikKaHJkX2ZiaV9oaWdoaW1tdW5lX2dlbmVzIDwtIHRvcFRhYmxlKGZpdF90cmVuZF9jb250cmFzdHMsIGNvZWYgPSAiY2x1c3QyIC0gY2x1c3QxIiwgbnVtYmVyID0gSW5mLCBzb3J0PSJwIikKYGBgCgpgYGB7cn0KaHJkX2ZiaV9wYXRod2F5cyA8LSBwYXRod2F5X2FuYWx5c2lzKGhyZF9mYmlfZ2VuZXMsIGdzX3R5cGUgPSAiZ28iKQpmYmlfaW1tdW5lX3BhdGh3YXlzIDwtIHBhdGh3YXlfYW5hbHlzaXMoZmJpX2ltbXVuZV9nZW5lcywgZ3NfdHlwZSA9ICJnbyIpCmhyZF9mYmlfaGlnaGltbXVuZV9wYXRod2F5cyA8LSBwYXRod2F5X2FuYWx5c2lzKGhyZF9mYmlfaGlnaGltbXVuZV9nZW5lcywgZ3NfdHlwZSA9ICJnbyIpCmBgYAoKIyMjIyMgSFJEIHZzLiBGQkkgc2FtcGxlcyAoY2x1c3QyIHZzLiBjbHVzdDErY2x1c3QzKQoKYGBge3J9CmRhdGF0YWJsZShocmRfZmJpX3BhdGh3YXlzLCBleHRlbnNpb25zID0gIkJ1dHRvbnMiLCBvcHRpb25zPWxpc3QocGFnZUxlbmd0aD01LCBzY3JvbGxYPVRSVUUsZG9tPSdCZnJ0aXAnLGJ1dHRvbnMgPSBjKCdjb3B5JywgJ2NzdicsICdleGNlbCcsICdwZGYnLCAncHJpbnQnKSkpCmBgYAoKCgojIyMjIyBIaWdoIGltbXVuZSBGQkkgdnMuIGxvdyBpbW11bmUgRkJJIChjbHVzdDEgdnMuIGNsdXN0MykKCmBgYHtyfQpkYXRhdGFibGUoZmJpX2ltbXVuZV9wYXRod2F5cywgZXh0ZW5zaW9ucyA9ICJCdXR0b25zIiwgb3B0aW9ucz1saXN0KHBhZ2VMZW5ndGg9NSwgc2Nyb2xsWD1UUlVFLGRvbT0nQmZydGlwJyxidXR0b25zID0gYygnY29weScsICdjc3YnLCAnZXhjZWwnLCAncGRmJywgJ3ByaW50JykpKQpgYGAKCgojIyMjIyBIUkQgdnMuIGhpZ2ggaW1tdW5lIEZCSSAoY2x1c3QyIHZzLiBjbHVzdDEpCgpgYGB7cn0KZGF0YXRhYmxlKGhyZF9mYmlfaGlnaGltbXVuZV9wYXRod2F5cywgZXh0ZW5zaW9ucyA9ICJCdXR0b25zIiwgb3B0aW9ucz1saXN0KHBhZ2VMZW5ndGg9NSwgc2Nyb2xsWD1UUlVFLGRvbT0nQmZydGlwJyxidXR0b25zID0gYygnY29weScsICdjc3YnLCAnZXhjZWwnLCAncGRmJywgJ3ByaW50JykpKQpgYGAKCiMjIyMgdm9vbQoKV2UgaGF2ZSBncmVhdGVyIHRoYW4gMy1mb2xkIHZhcmlhYmlsaXR5IGluIGxpYnJhcnkgc2l6ZSwgc28gd2UncmUgYmV0dGVyIG9mZiB1c2luZyB0aGUgdm9vbSBtZXRob2QgZm9yIERFIGFuYWx5c2lzLiAKCmBgYHtyfQpmaXRfdm9vbSA8LSBybmFzZXFfREVfaW5pdGlhbF9maXQoZGdlLCBkZXNpZ24sIHR5cGUgPSAidm9vbSIpCmBgYAoKYGBge3J9CmZpdF92b29tX2NvbnRyYXN0cyA8LSBjb250cmFzdHMuZml0KGZpdF92b29tLCBjb250cmFzdC5tYXRyaXgpCmZpdF92b29tX2NvbnRyYXN0cyA8LSBlQmF5ZXMoZml0X3Zvb21fY29udHJhc3RzKQoKaHJkX2ZiaV9nZW5lcyA8LSB0b3BUYWJsZShmaXRfdm9vbV9jb250cmFzdHMsIGNvZWYgPSAiY2x1c3QyIC0gKGNsdXN0MSArIGNsdXN0MykvMiIsIGFkanVzdC5tZXRob2QgPSAiQkgiLCBudW1iZXIgPUluZiwgc29ydD0icCIpCmZiaV9pbW11bmVfZ2VuZXMgPC0gdG9wVGFibGUoZml0X3Zvb21fY29udHJhc3RzLCBjb2VmID0gImNsdXN0MSAtIGNsdXN0MyIsIG51bWJlciA9IEluZiwgc29ydD0icCIpCmhyZF9mYmlfaGlnaGltbXVuZV9nZW5lcyA8LSB0b3BUYWJsZShmaXRfdm9vbV9jb250cmFzdHMsIGNvZWYgPSAiY2x1c3QyIC0gY2x1c3QxIiwgbnVtYmVyID0gSW5mLCBzb3J0PSJwIikKYGBgCgoKYGBge3IsIG1lc3NhZ2U9VFJVRSwgd2FybmluZz1UUlVFfQpocmRfZmJpX3BhdGh3YXlzIDwtIHBhdGh3YXlfYW5hbHlzaXMoaHJkX2ZiaV9nZW5lcywgZ3NfdHlwZSA9ICJrZWdnIikKZmJpX2ltbXVuZV9wYXRod2F5cyA8LSBwYXRod2F5X2FuYWx5c2lzKGZiaV9pbW11bmVfZ2VuZXMsIGdzX3R5cGUgPSAia2VnZyIpCmhyZF9mYmlfaGlnaGltbXVuZV9wYXRod2F5cyA8LSBwYXRod2F5X2FuYWx5c2lzKGhyZF9mYmlfaGlnaGltbXVuZV9nZW5lcywgZ3NfdHlwZSA9ICJrZWdnIikKIyBwdi5vdXQubGlzdCA8LSBzYXBwbHkocGF0aC5pZHMyLCBmdW5jdGlvbihwaWQpIHBhdGh2aWV3KAojICAgICAgICAgICAgICAgICAgICAgICBnZW5lLmRhdGEgPSAgZXhwLmZjLCBwYXRod2F5LmlkID0gcGlkLAojICAgICAgICAgICAgICAgICAgICAgICBzcGVjaWVzID0gImhzYSIsIG91dC5zdWZmaXg9b3V0LnN1ZmZpeCkpCmBgYAoKKipOb3RlOioqIEJSQ0ExLCBCUkNBMiwgYW5kIFJQQSBhcmUgYWxzbyBkb3ducmVndWxhdGVkIGluIHRoZSBIUkQgZ3JvdXAgLS0gdGhlIGVudGlyZSBIUi1hc3NvY2lhdGVkIGdlbmUgY2x1c3RlciBpc24ndCBzaWduaWZpY2FudGx5IGRvd25yZWd1bGF0ZWQgdGhvdWdoLiAKCk9uZSBvZiB0aGUga2V5IHJlYXNvbnMgd2h5IHRoZSBlbnRpcmUgSFIgcGF0aHdheSBpc24ndCBzaWduaWZpY2FudGx5IGRvd25yZWd1bGF0ZWQgaXMgYmVjYXVzZSBQb2xRIGlzIHBhcnQgb2YgaXQgYW5kIFBvbFEgaXMgdXByZWd1bGF0ZWQuIE9mIG5vdGUsIFBvbFEgcHJvbW90ZXMgTU1FSiwgcHJvdmlkaW5nIGZ1cnRoZXIgZXZpZGVuY2UgZm9yIE1NRUogYWN0aXZpdHkgaW4gRkJJIHR1bW91cnMuIAoKIyMjIyMgSFJEIHZzLiBGQkkgc2FtcGxlcyAoY2x1c3QyIHZzLiBjbHVzdDErY2x1c3QzKQoKYGBge3J9CmRhdGF0YWJsZShocmRfZmJpX3BhdGh3YXlzLCBleHRlbnNpb25zID0gIkJ1dHRvbnMiLCBvcHRpb25zPWxpc3QocGFnZUxlbmd0aD01LCBzY3JvbGxYPVRSVUUsZG9tPSdCZnJ0aXAnLGJ1dHRvbnMgPSBjKCdjb3B5JywgJ2NzdicsICdleGNlbCcsICdwZGYnLCAncHJpbnQnKSkpCmBgYAoKCgojIyMjIyBIaWdoIGltbXVuZSBGQkkgdnMuIGxvdyBpbW11bmUgRkJJIChjbHVzdDEgdnMuIGNsdXN0MykKCmBgYHtyfQpkYXRhdGFibGUoZmJpX2ltbXVuZV9wYXRod2F5cywgZXh0ZW5zaW9ucyA9ICJCdXR0b25zIiwgb3B0aW9ucz1saXN0KHBhZ2VMZW5ndGg9NSwgc2Nyb2xsWD1UUlVFLGRvbT0nQmZydGlwJyxidXR0b25zID0gYygnY29weScsICdjc3YnLCAnZXhjZWwnLCAncGRmJywgJ3ByaW50JykpKQpgYGAKCgojIyMjIyBIUkQgdnMuIGhpZ2ggaW1tdW5lIEZCSSAoY2x1c3QyIHZzLiBjbHVzdDEpCgpgYGB7cn0KZGF0YXRhYmxlKGhyZF9mYmlfaGlnaGltbXVuZV9wYXRod2F5cywgZXh0ZW5zaW9ucyA9ICJCdXR0b25zIiwgb3B0aW9ucz1saXN0KHBhZ2VMZW5ndGg9NSwgc2Nyb2xsWD1UUlVFLGRvbT0nQmZydGlwJyxidXR0b25zID0gYygnY29weScsICdjc3YnLCAnZXhjZWwnLCAncGRmJywgJ3ByaW50JykpKQpgYGAKCgojIyMjIFNvbWUgRE5BIHJlcGFpciBnZW5lcwoKYGBge3J9CnYgPC0gdm9vbShpY2djX2NvdW50c19maWx0ZXJlZCwgZGVzaWduLCBub3JtYWxpemU9InF1YW50aWxlIiwgcGxvdD1GQUxTRSkKYGBgCgpgYGB7cn0KaWNnY19leHByX2RmX3dpZGUgPC0gdiRFICU+JSB0ICU+JSBhcy5kYXRhLmZyYW1lICU+JSByb3duYW1lc190b19jb2x1bW4oImljZ2NfZG9ub3JfaWQiKQoKaWNnY19leHByX2RmX21lbHRlZCA8LSByZXNoYXBlMjo6bWVsdChpY2djX2V4cHJfZGZfd2lkZSwgaWQudmFycyA9IGMoImljZ2NfZG9ub3JfaWQiKSwgbWVhc3VyZS52YXJzID0gY29sbmFtZXMoaWNnY19leHByX2RmX3dpZGUpWzI6bmNvbChpY2djX2V4cHJfZGZfd2lkZSldLCB2YXJpYWJsZS5uYW1lID0gIk5hbWUiLCB2YWx1ZS5uYW1lID0gImV4cHJlc3Npb24iKQoKaWNnY19leHByX2RmX21lbHRlZF9sYWJlbGVkIDwtIHBseXI6OmpvaW4oaWNnY19leHByX2RmX21lbHRlZCwgY2x1c3RlcnMpCgpSRVBBSVJfR0VORVMgPC0gYygiQlJDQTEiLCAiQlJDQTIiLCAiUE9MUSIsICJGRU4xIiwgIlhSQ0MxIiwgIlBBUlAxIiwgIkxJRzMiKQoKaWNnY19leHByX2RmX21lbHRlZF9sYWJlbGVkX2ZpbHRlcmVkIDwtIHN1YnNldChpY2djX2V4cHJfZGZfbWVsdGVkX2xhYmVsZWQsIE5hbWUgJWluJSBSRVBBSVJfR0VORVMpCgpnZ3Bsb3Qoc3Vic2V0KGljZ2NfZXhwcl9kZl9tZWx0ZWRfbGFiZWxlZF9maWx0ZXJlZCwgIWlzLm5hKGNsdXN0ZXIpKSwgYWVzKHg9Y2x1c3RlciwgeT1leHByZXNzaW9uKSkgKyBnZW9tX2JveHBsb3QoKSArIGdlb21faml0dGVyKHBvc2l0aW9uPXBvc2l0aW9uX2ppdHRlcih3aWR0aD0wLjIsIGhlaWdodD0wKSkgKyB0aGVtZV9idygpICsgdGhlbWVfUHVibGljYXRpb24oKSArIGZhY2V0X3dyYXAofiBOYW1lLCBzY2FsZXMgPSAiZnJlZSIpCmBgYAoKIyMjIFN1cnZpdmFsCgpgYGB7cn0KaWNnY19jbGluaWNhbCA8LSByZWFkX2Rvbm9yX3NwZWNpbWVuX3N1cnZpdmFsKGljZ2NfZG9ub3JfZmlsZSwgaWNnY19zcGVjaW1lbl9maWxlKQoKaWNnY19jbGluaWNhbF9sYWJlbGVkIDwtIHBseXI6OmpvaW4oY2x1c3RlcnMsIGljZ2NfY2xpbmljYWwpCgppY2djX2NsaW5pY2FsX2xhYmVsZWQkU3Vydk9iaiA8LSB3aXRoKGljZ2NfY2xpbmljYWxfbGFiZWxlZCwgU3Vydihkb25vcl9zdXJ2aXZhbF90aW1lLCBkb25vcl92aXRhbF9zdGF0dXMgPT0gImRlY2Vhc2VkIikpCmBgYAoKU3Vydml2YWwgYnkgRkJJIHN0YXR1cyAuLi4KCmBgYHtyfQpzaW1wbGVfc3Vydml2YWxfYW5hbHlzaXMoU3Vydk9iaiB+IGNsdXN0ZXIgIT0gMiwgZGF0YSA9IGljZ2NfY2xpbmljYWxfbGFiZWxlZCkKYGBgCgpgYGB7cn0Kc2ltcGxlX3N1cnZpdmFsX2FuYWx5c2lzKFN1cnZPYmogfiBjbHVzdGVyLCBkYXRhID0gaWNnY19jbGluaWNhbF9sYWJlbGVkKQpgYGAKCiMjIFRDR0EgdmFsaWRhdGlvbgoKV2hpbGUgd2UgY2FuJ3QgZGlyZWN0bHkgZG8gbXV0YXRpb24gc2lnbmF0dXJlIGFuYWx5c2lzIHdpdGggTU1DVE0gb24gZXhvbWUgZGF0YSAoSSBzdXBwb3NlIHRoaXMgaXMgdGhlb3JldGljYWxseSBwb3NzaWJsZSBidXQgd2UnZCBwcm9iYWJseSBiZSByZXN0cmljdGVkIGluIHRoZSB0eXBlcyBvZiBldmVudHMgd2UgY2FuIGNhbGwsIGxpa2UgbG9uZyBTVnMpLCB3ZSBjYW4gbG9vayBhdCB0aGUgRkJJLUhMQU1QIGZpbmRpbmcgZnJvbSBZaWthbidzIHBhcGVyIGFuZCBzZWUgaWYgdGhhdCBsaW5lcyB1cCB3aXRoIGltbXVuZSBzaWduYXR1cmVzLiAKCmBgYHtyfQp0Y2dhX2V4cHJfbWF0IDwtIHJlYWRfdGNnYV9leHBycyh0Y2dhX2V4cHJfbWF0X2ZpbGUpCnRjZ2Ffb3ZfYW5ub3RhdGlvbiA8LSBmcmVhZCh0Y2dhX292X2Fubm90YXRpb25fZmlsZSkKI3RjZ2FfZmJpX2hsYW1wX3Byb3BvcnRpb25zIDwtIGZyZWFkKHRjZ2FfZmJpX2hsYW1wX3Byb3BvcnRpb25zX2ZpbGUpCmBgYAoKYGBge3J9CnRjZ2FfZXhwcl9kZiA8LSB0Y2dhX2V4cHJfbWF0ICU+JSBhcy5kYXRhLmZyYW1lICU+JSBjb2x1bW5fdG9fcm93bmFtZXMoInRjZ2Ffc2FtcGxlX2lkIikgJT4lIHQgJT4lIGFzLmRhdGEuZnJhbWUgJT4lIHJvd25hbWVzX3RvX2NvbHVtbigiTmFtZSIpCnRjZ2FfY2VsbHR5cGVfbWF0cml4IDwtIGNyZWF0ZV9wYXRod2F5X21hdHJpeCh0Y2dhX2V4cHJfZGYsIGxhYmVscywgZGJfcGF0aCwgY29udmVydF9pZHMgPSBGQUxTRSkKCnRjZ2FfaW1tdW5lIDwtIHJvd25hbWVzX3RvX2NvbHVtbihhcy5kYXRhLmZyYW1lKHQodGNnYV9jZWxsdHlwZV9tYXRyaXgpKSwgdmFyID0gInRjZ2Ffc2FtcGxlX2lkIikKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9MTZ9Cm1hdCA8LSB0Y2dhX2ltbXVuZSAlPiUgY29sdW1uX3RvX3Jvd25hbWVzKCJ0Y2dhX3NhbXBsZV9pZCIpICU+JSBzY2FsZSAjJT4lIGNsaXBfdmFsdWVzKDIsIC0yKQpyb3dfYW5ub3RhdGlvbnMgPC0gdGNnYV9vdl9hbm5vdGF0aW9uICU+JSBhcy5kYXRhLmZyYW1lICU+JSBjb2x1bW5fdG9fcm93bmFtZXMoInRjZ2Ffc2FtcGxlX2lkIikKCnRjZ2FoZWF0IDwtIHBoZWF0bWFwKG1hdCwgYW5ub3RhdGlvbl9yb3cgPSByb3dfYW5ub3RhdGlvbnMsIGNsdXN0ZXJfcm93cyA9IFRSVUUsIGNsdXN0ZXJfY29scyA9IFRSVUUsIGNsdXN0ZXJpbmdfbWV0aG9kID0gIndhcmQuRCIsIHNob3dfcm93bmFtZXMgPSBGQUxTRSkKCiN0Y2dhX2NsdXN0ZXJzIDwtIHJvd25hbWVzX3RvX2NvbHVtbihkYXRhLmZyYW1lKGNsdXN0ZXI9ZmFjdG9yKGN1dHJlZSh0Y2dhaGVhdCR0cmVlX3JvdywgMikpKSwgdmFyID0gInRjZ2Ffc2FtcGxlX2lkIikKCmNvbCA8LSAiQ3l0b3RveGljaXR5Igp0Y2dhX2NsdXN0ZXJzIDwtIHJvd25hbWVzX3RvX2NvbHVtbihkYXRhLmZyYW1lKGNsdXN0ZXI9bWF0Wyxjb2xdID4gbWVkaWFuKG1hdFssY29sXSkpLCAidGNnYV9zYW1wbGVfaWQiKSAlPiUgbXV0YXRlKGNsdXN0ZXIgPSBhcy5mYWN0b3IoYXMubnVtZXJpYyhjbHVzdGVyKSkpCgojcm93X2Fubm90YXRpb25zIDwtIHBseXI6OmpvaW4odGNnYV9vdl9hbm5vdGF0aW9uLCB0Y2dhX2NsdXN0ZXJzKSAlPiUgYXMuZGF0YS5mcmFtZSAlPiUgY29sdW1uX3RvX3Jvd25hbWVzKCJ0Y2dhX3NhbXBsZV9pZCIpCiNwaGVhdG1hcChtYXQsIGFubm90YXRpb25fcm93ID0gcm93X2Fubm90YXRpb25zLCBjbHVzdGVyX3Jvd3MgPSBUUlVFLCBjbHVzdGVyX2NvbHMgPSBUUlVFLCBjbHVzdGVyaW5nX21ldGhvZCA9ICJ3YXJkLkQiLCBzaG93X3Jvd25hbWVzID0gRkFMU0UpCmBgYAoKCmBgYHtyfQpkZl9tZXJnZWQgPC0gcGx5cjo6am9pbih0Y2dhX2ltbXVuZSwgdGNnYV9vdl9hbm5vdGF0aW9uKQoKZXhwcl9tZWFzdXJlX3ZhcnMgPC0gY29sbmFtZXModGNnYV9pbW11bmUpWzI6bmNvbCh0Y2dhX2ltbXVuZSldCgpkZl9tZXJnZWRfbWVsdGVkIDwtIG1lbHQoZGZfbWVyZ2VkLCBpZC52YXJzID0gYygidGNnYV9zYW1wbGVfaWQiLCAiU3ViZ3JvdXAiLCAiTW9sZWN1bGFyU3VidHlwZSIsICJCUkNBLnN0YXR1cyIpLCBtZWFzdXJlLnZhcnMgPSBleHByX21lYXN1cmVfdmFycywgdmFyaWFibGUubmFtZSA9ICJNZWFzdXJlIiwgdmFsdWUubmFtZSA9ICJFeHByZXNzaW9uIikKYGBgCgpgYGB7ciwgZmlnLmhlaWdodCA9IDE4fQpwdmFscyA8LSBzZXROYW1lcyhkZHBseShzdWJzZXQoZGZfbWVyZ2VkX21lbHRlZCwgIWlzLm5hKFN1Ymdyb3VwKSksIC4oTWVhc3VyZSksIGZ1bmN0aW9uKHgpIHsKICBkZiA8LSBhcy5kYXRhLmZyYW1lKHgpCiAgdGVzdHJlcyA8LSBrcnVza2FsLnRlc3QoRXhwcmVzc2lvbiB+IGZhY3RvcihTdWJncm91cCksIGRhdGEgPSBkZikKICAKICBwdmFsIDwtIHRlc3RyZXMkcC52YWx1ZQogIGVxIDwtIHN1YnN0aXR1dGUoaXRhbGljKFApPT1wLCBsaXN0KHA9Zm9ybWF0KHB2YWwsIGRpZ2l0cz0zKSkpCiAgcmV0dXJuKGFzLmNoYXJhY3Rlcihhcy5leHByZXNzaW9uKGVxKSkpCn0pLCBjKCJNZWFzdXJlIiwgInAudmFsdWUiKSkKCmdncGxvdChzdWJzZXQoZGZfbWVyZ2VkX21lbHRlZCwgIWlzLm5hKFN1Ymdyb3VwKSksIGFlcyh4PVN1Ymdyb3VwLCB5PUV4cHJlc3Npb24pKSArIGdlb21fYm94cGxvdCgpICsgdGhlbWVfYncoKSArIGZhY2V0X3dyYXAofiBNZWFzdXJlLCBzY2FsZXMgPSAiZnJlZSIsIG5jb2w9MykgKyB0aGVtZV9QdWJsaWNhdGlvbigpICsgZ2VvbV90ZXh0KGRhdGE9cHZhbHMsIGFlcyh4PUluZiwgeT1JbmYsIGxhYmVsPXAudmFsdWUpLCBoanVzdD0xLjEsIHZqdXN0PTEuNSxzaXplPTMscGFyc2U9VFJVRSkgCmBgYAoKUC12YWx1ZXMgYXJlIHVuY29ycmVjdGVkLiBTbyBhZnRlciBjb3JyZWN0aW9uLCB3ZSBhcmVuJ3QgZ29pbmcgdG8gZ2V0IGFueXRoaW5nIHNpZ25pZmljYW50LiAKCkluIGNvbmNsdXNpb24sIHRoZSBpbW11bmUgc3ViZ3JvdXBzIGNvbnN0aXR1dGUgYSBuZXcgc3ViZ3JvdXBpbmcgaW5kZXBlbmRlbnQgb2YgRkJJLUhMQU1QLiAKCkkndmUgYWxzbyBkb25lIGNvcnJlbGF0aW9uIHRlc3RpbmcgdXNpbmcgdGhlIGxvZ1IgdmFsdWVzIHRoYXQgWWlrYW4ncyBwcm92aWRlZCBtZSAocmF0aGVyIHRoYW4gdGhlIGRpc2NyZXRlIGdyb3VwaW5ncyBvZiBubyBBTVAsIEZCSS1BTVAgbG93LCBhbmQgRkJJLUFNUCBoaWdoKSwgYnV0IHRoZSBjb3JyZWxhdGlvbnMgYXJlIHZlcnkgcG9vciAocmhvIHZhbHVlcyBvZiB+IC0wLjA1IG9yIHNvLCBwLXZhbHVlcyBvZiA+PTAuMykuIAoKIyMjIFN1cnZpdmFsCgoKYGBge3J9CnRjZ2FfY2xpbmljYWwgPC0gcmVhZF90Y2dhX2NsaW5pY2FsKHRjZ2FfZG9ub3JfZmlsZSwgdHlwZSA9ICJzeW5hcHNlMiIsIGZpbHRlciA9IFRSVUUsIHVuaXF1ZSA9IEZBTFNFKQoKdGNnYV9jbGluaWNhbF9sYWJlbGVkIDwtIFJlZHVjZShmdW5jdGlvbih4LHkpIHBseXI6OmpvaW4oeCx5LCB0eXBlID0gJ2Z1bGwnKSwgbGlzdCh0Y2dhX2NsdXN0ZXJzLCB0Y2dhX2NsaW5pY2FsLCB0Y2dhX292X2Fubm90YXRpb24pKQoKdGNnYV9jbGluaWNhbF9sYWJlbGVkJFN1cnZPYmogPC0gd2l0aCh0Y2dhX2NsaW5pY2FsX2xhYmVsZWQsIFN1cnYoaWZlbHNlKHZpdGFsX3N0YXR1cyA9PSAiRGVhZCIsIGRlYXRoX2RheXNfdG8sIGxhc3RfY29udGFjdF9kYXlzX3RvKSwgdml0YWxfc3RhdHVzID09ICJEZWFkIikpCgojdGNnYV9jbGluaWNhbF9sYWJlbGVkIDwtIHN1YnNldCh0Y2dhX2NsaW5pY2FsX2xhYmVsZWQsIGFkZGl0aW9uYWxfZHJ1Z190aGVyYXB5ID09ICJOTyIgJiBhZGRpdGlvbmFsX2ltbXVub190aGVyYXB5ID09ICJOTyIgJiBhZGRpdGlvbmFsX3BoYXJtYWNldXRpY2FsX3RoZXJhcHkgPT0gIk5PIiAmICB0YXJnZXRlZF9tb2xlY3VsYXJfdGhlcmFweSA9PSAiTk8iICYgcmFkaWF0aW9uX3RoZXJhcHkgPT0gIk5PIikKI3RjZ2FfY2xpbmljYWxfbGFiZWxlZCA8LSBzdWJzZXQodGNnYV9jbGluaWNhbF9sYWJlbGVkLCBzdHJfZGV0ZWN0KHR1bW9yX3N0YWdlLCAiXklJSSIpKQpgYGAKCiMjIyMgRkJJLUFNUCBjb2xvY2FsaXphdGlvbgoKTGV0J3MgZmlyc3Qgc3RyYXRpZnkgYnkgWWlrYW4ncyBncm91cGluZ3M6CgpgYGB7cn0Kc2ltcGxlX3N1cnZpdmFsX2FuYWx5c2lzKFN1cnZPYmogfiBTdWJncm91cCwgZGF0YSA9IHRjZ2FfY2xpbmljYWxfbGFiZWxlZCkKYGBgCgpOb3RlOiBUaGUgbG9ncmFuayBwLXZhbHVlcyBpbiB0aGUgcGFwZXIgYXJlIGluY29ycmVjdCwgdGhlIG9uZSdzIEkndmUgY29tcHV0ZWQgYXJlIHRoZSByaWdodCBvbmVzLiBJbiB0aGUgcGFwZXIsIHRoZSBkZWF0aCBkYXlzIGZyb20gdGhlIGRlYWQgcGF0aWVudHMgd2VyZSBub3QgY29ycmVjdGx5IGNvbWJpbmVkIGludG8gdGhlIGNvbXB1dGF0aW9uLCBzbyBwLXZhbHVlcyB3ZXJlIGNvbXB1dGVkIHdpdGhvdXQgdGhlIGRlYWQgcGF0aWVudHMuIAoKIyMjIyBJbW11bmUgYWN0aXZpdHkKCldlJ2xsIG5vdyBzdHJhdGlmeSBieSBgY2x1c3RlcmAsIGEgdmFyaWFibGUgdGhhdCBpbmRpY2F0ZXMgd2hldGhlciBvciBub3Qgd2UncmUgYWJvdmUgbWVkaWFuIGluIHRlcm1zIG9mIGByIGNvbGAuIEluIG90aGVyIHdvcmRzLCAxIG1lYW5zIHdlJ3JlIGhpZ2ggaW1tdW5lLCBhbmQgMCBtZWFucyB3ZSdyZSBsb3cgaW1tdW5lLiAKCmBgYHtyfQpzaW1wbGVfc3Vydml2YWxfYW5hbHlzaXMoU3Vydk9iaiB+IGNsdXN0ZXIsIGRhdGEgPSB0Y2dhX2NsaW5pY2FsX2xhYmVsZWQpCmBgYAoKTG93IGltbXVuZSB0dW1vdXJzIHRyZW5kIHRvd2FyZHMgcG9vciBzdXJ2aXZhbCBidXQgdGhpcyBpcyBub3Qgc2lnbmlmaWNhbnQgYXQgYWxsLiAKCk5vdyBsZXQncyB0cnkgYnkgdHJlYXRpbmcgaW1tdW5lIGFjdGl2aXR5IGFzIGEgY29udGludW91cyB2YXJpYWJsZSwgY29udHJvbGxpbmcgZm9yIG90aGVyIGNvdmFyaWF0ZXM6CgpgYGB7cn0KdGNnYV9pbW11bmVfY29udGludW91cyA8LSAgdGNnYV9jZWxsdHlwZV9tYXRyaXggJT4lIHQgJT4lIGFzLmRhdGEuZnJhbWUgJT4lIHJvd25hbWVzX3RvX2NvbHVtbigidGNnYV9zYW1wbGVfaWQiKQoKdGNnYV9jbGluaWNhbF9sYWJlbGVkX2NvbnRpbnVvdXMgPC0gcGx5cjo6am9pbih0Y2dhX2NsaW5pY2FsX2xhYmVsZWQsIHRjZ2FfaW1tdW5lX2NvbnRpbnVvdXMpCgpjb3hwaChTdXJ2T2JqIH4gQ3l0b3RveGljaXR5ICsgYWdlX2F0X2luaXRpYWxfcGF0aG9sb2dpY19kaWFnbm9zaXMgKyB0dW1vcl9ncmFkZSArIHN0cl9leHRyYWN0KHR1bW9yX3N0YWdlLCAiW0lWXSsiKSArIGFkZGl0aW9uYWxfY2hlbW9fdGhlcmFweSArIGFkZGl0aW9uYWxfZHJ1Z190aGVyYXB5ICsgYWRkaXRpb25hbF9pbW11bm9fdGhlcmFweSwgdGNnYV9jbGluaWNhbF9sYWJlbGVkX2NvbnRpbnVvdXMpCmBgYAoKSW5kZWVkLCBpbW11bmUgYWN0aXZpdHkgaXMgc2lnbmlmaWNhbnRseSBhc3NvY2lhdGVkIHdpdGggcHJvbG9uZ2VkIHN1cnZpdmFsIChuZWdhdGl2ZSBjb2VmZmljaWVudCA9IGZld2VyIGRlYXRoIGV2ZW50cykuIAoKIyMjIyBTdHJhdGlmaWNhdGlvbiBieSBmb2xkYmFjay1BTVAgc3RhdHVzCgpgYGB7cn0KZmJpX2hpZ2ggPC0gc3Vic2V0KHRjZ2FfY2xpbmljYWxfbGFiZWxlZCwgU3ViZ3JvdXAgPT0gIkZCSS1BTVAgSGlnaCIpCmZiaV9sb3cgPC0gc3Vic2V0KHRjZ2FfY2xpbmljYWxfbGFiZWxlZCwgU3ViZ3JvdXAgPT0gIkZCSS1BTVAgTG93IikKZmJpX25vIDwtIHN1YnNldCh0Y2dhX2NsaW5pY2FsX2xhYmVsZWQsIFN1Ymdyb3VwID09ICJObyBBTVAiKQpmYmlfbm90aGlnaCA8LSBzdWJzZXQodGNnYV9jbGluaWNhbF9sYWJlbGVkLCBTdWJncm91cCAlaW4lIGMoIkZCSS1BTVAgTG93IiwgIk5vIEFNUCIpKQpgYGAKCmBgYHtyfQpzaW1wbGVfc3Vydml2YWxfYW5hbHlzaXMoU3Vydk9iaiB+IGNsdXN0ZXIsIGRhdGEgPSBmYmlfaGlnaCkKYGBgCgpgYGB7cn0Kc2ltcGxlX3N1cnZpdmFsX2FuYWx5c2lzKFN1cnZPYmogfiBjbHVzdGVyLCBkYXRhID0gZmJpX2xvdykKYGBgCgpgYGB7cn0Kc2ltcGxlX3N1cnZpdmFsX2FuYWx5c2lzKFN1cnZPYmogfiBjbHVzdGVyLCBkYXRhID0gZmJpX25vKQpgYGAKCldoYXQncyBjdXJpb3VzIGFib3V0IHRoaXMgaXMgdGhhdCBpdCBzZWVtcyBvbmx5IHRoZSBubyBBTVAgZ3JvdXAgZ2V0cyBhbnkgYmVuZWZpdCBmcm9tIGhhdmluZyBhIGhpZ2ggaW1tdW5lIHJlc3BvbnNlLiBTbyB0aGVyZSdzIG5vIGJlbmVmaXQgd2l0aGluIHRoZSBmb2xkYmFjayBncm91cCBvZiBoaWdoL2xvdyBpbW11bmUgcmVzcG9uc2UuIApgYGB7cn0Kc2ltcGxlX3N1cnZpdmFsX2FuYWx5c2lzKFN1cnZPYmogfiBjbHVzdGVyLCBkYXRhID0gZmJpX25vdGhpZ2gpCmBgYAoKIyMjIyBTdHJhdGlmaWNhdGlvbiBieSBpbW11bmUgYWN0aXZpdHkKCmBgYHtyfQppbW11bmVfbG93IDwtIHN1YnNldCh0Y2dhX2NsaW5pY2FsX2xhYmVsZWQsIGNsdXN0ZXIgPT0gMCkKaW1tdW5lX2hpZ2ggPC0gc3Vic2V0KHRjZ2FfY2xpbmljYWxfbGFiZWxlZCwgY2x1c3RlciA9PSAxKQpgYGAKCmBgYHtyfQpzaW1wbGVfc3Vydml2YWxfYW5hbHlzaXMoU3Vydk9iaiB+IFN1Ymdyb3VwLCBkYXRhID0gaW1tdW5lX2hpZ2gpCgpzaW1wbGVfc3Vydml2YWxfYW5hbHlzaXMoU3Vydk9iaiB+IFN1Ymdyb3VwLCBkYXRhID0gaW1tdW5lX2xvdykKYGBgCgpMaWtld2lzZSwgdGhlIG9ubHkgYmVuZWZpdCBvZiBiZWluZyBub24tZm9sZGJhY2sgaXMgZGVyaXZlZCBmcm9tIHRoZSBpbW11bmUtaGlnaCBncm91cCAtLSB0aGVyZSdzIG5vIGRpZmZlcmVuY2UgYmV0d2VlbiBiZWluZyBmb2xkYmFjayBvciBub3QgaWYgeW91J3JlIGluIHRoZSBsb3cgaW1tdW5lIGdyb3VwLiAKCiMjIyMgTXVsdGl2YXJpYXRlIG1vZGVscwoKYGBge3J9CnNpbXBsZV9zdXJ2aXZhbF9hbmFseXNpcyhTdXJ2T2JqIH4gU3ViZ3JvdXAgKyBjbHVzdGVyLCBkYXRhID0gdGNnYV9jbGluaWNhbF9sYWJlbGVkKQpgYGAKCldlJ2xsIG5leHQgY29tYmluZWQgRkJJLUFNUCBsb3cgYW5kIE5vIEFNUCBpbnRvIGEgc2luZ2xlIGNhdGVnb3J5IGNhbGxlZCBGQkktTm90SGlnaC4gCgpgYGB7cn0KdGNnYV9jbGluaWNhbF9sYWJlbGVkJG5ld2dyb3VwIDwtIGlmZWxzZSh0Y2dhX2NsaW5pY2FsX2xhYmVsZWQkU3ViZ3JvdXAgPT0gIkZCSS1BTVAgSGlnaCIsICJGQkktSGlnaCIsICJGQkktTm90SGlnaCIpCgpzaW1wbGVfc3Vydml2YWxfYW5hbHlzaXMoU3Vydk9iaiB+IG5ld2dyb3VwICsgY2x1c3RlciwgZGF0YSA9IHRjZ2FfY2xpbmljYWxfbGFiZWxlZCkKYGBgCgoKTGV0J3MgYWxzbyBkbyBDb3ggcHJvcG9ydGlvbmFsIGhhemFyZHMgbW9kZWxzOgoKYGBge3J9Cm1vZDEgPC0gY294cGgoU3Vydk9iaiB+IEN5dG90b3hpY2l0eSArIFN1Ymdyb3VwICsgYWdlX2F0X2luaXRpYWxfcGF0aG9sb2dpY19kaWFnbm9zaXMgKyB0dW1vcl9ncmFkZSArIHN0cl9leHRyYWN0KHR1bW9yX3N0YWdlLCAiW0lWXSsiKSArIGFkZGl0aW9uYWxfY2hlbW9fdGhlcmFweSArIGFkZGl0aW9uYWxfZHJ1Z190aGVyYXB5ICsgYWRkaXRpb25hbF9pbW11bm9fdGhlcmFweSwgdGNnYV9jbGluaWNhbF9sYWJlbGVkX2NvbnRpbnVvdXMpCmFub3ZhKG1vZDEpCmBgYAoKYGBge3J9Cm1vZDIgPC0gY294cGgoU3Vydk9iaiB+IEN5dG90b3hpY2l0eSArIFN1Ymdyb3VwICsgYWdlX2F0X2luaXRpYWxfcGF0aG9sb2dpY19kaWFnbm9zaXMsIHRjZ2FfY2xpbmljYWxfbGFiZWxlZF9jb250aW51b3VzKQphbm92YShtb2QyKQpgYGAKCmBgYHtyfQptb2QzIDwtIGNveHBoKFN1cnZPYmogfiBDeXRvdG94aWNpdHkgKyAoU3ViZ3JvdXAgPT0gIkZCSS1BTVAgSGlnaCIpICsgYWdlX2F0X2luaXRpYWxfcGF0aG9sb2dpY19kaWFnbm9zaXMsIHN1YnNldCh0Y2dhX2NsaW5pY2FsX2xhYmVsZWRfY29udGludW91cywgIWlzLm5hKFN1Ymdyb3VwKSkpCmFub3ZhKG1vZDMpCmBgYAoKU28gdGhlIGVmZmVjdHMgb2YgaW1tdW5lIGFjdGl2aXR5IGFuZCBGQkkvSFJEIHN0YXR1cyBhcmUgY29sbGluZWFyLiAKCiMjIEZpbmFsIHJ1bgoKSW5zdGVhZCBvZiBvdmVyd3JpdGluZyB0aGUgcHJldmlvdXMsIHRoaXMgaXMgaXRzIG93biBzZWN0aW9uIHNpbmNlIHRoZSB1bmRlcmx5aW5nIGltcGxlbWVudGF0aW9uIGhhcyBjaGFuZ2VkIHN1YnN0YW50aWFsbHkuIAoKYGBge3J9CnZhcmlhbnRfdGFibGUgPC0gcmVhZF92YXJpYW50X2ZpbGUobWFzdGVyX3ZhcmlhbnRfZmlsZSwgZGJfcGF0aCkKYnJlYWtwb2ludF90YWJsZSA8LSByZWFkX3ZhcmlhbnRfZmlsZShtYXN0ZXJfYnJlYWtwb2ludF9maWxlLCBkYl9wYXRoKQoKc2lnX3Jlc3VsdHMgPC0gcmVhZF9zaWduYXR1cmVfZmlsZXMobW1jdG1fZmluYWxfcGF0aWVudF9kaXIsIHZhcmlhbnRfdGFibGUsIGJyZWFrcG9pbnRfdGFibGUsIGRiX3BhdGgpCgpzaWdfcmVzdWx0c19zbnYgPC0gc3Vic2V0KHNpZ19yZXN1bHRzJHNudiwgaXNfcHJlc2VudCA9PSAxKQpzaWdfcmVzdWx0c19zdiA8LSBzdWJzZXQoc2lnX3Jlc3VsdHMkc3YsIGlzX3ByZXNlbnQgPT0gMSkKc2lnX3Jlc3VsdHNfbm9uaXRoIDwtIHNpZ19yZXN1bHRzJG5vbml0aCAlPiUgYXMuZGF0YS5mcmFtZSAlPiUgY29sdW1uX3RvX3Jvd25hbWVzKCJzaWduYXR1cmUiKSAlPiUgdCAlPiUgYXMuZGF0YS5mcmFtZSAlPiUgcm93bmFtZXNfdG9fY29sdW1uKCJjb25kZW5zZWRfaWQiKQoKc2lnbmF0dXJlX2xhYmVscyA8LSBzdHJfZXh0cmFjdChjKGNvbG5hbWVzKHNpZ19yZXN1bHRzX3NudiksIGNvbG5hbWVzKHNpZ19yZXN1bHRzX3N2KSksICJTTj9WLVswLTldKyIpCnNpZ25hdHVyZV9sYWJlbHMgPC0gc2lnbmF0dXJlX2xhYmVsc1shaXMubmEoc2lnbmF0dXJlX2xhYmVscyldCmBgYAoKYGBge3J9CnN1bW1hcml6ZV9zaWduYXR1cmVfcHJvcG9ydGlvbnMgPC0gZnVuY3Rpb24oeCwgYnk9YygiY29uZGVuc2VkX2lkIiwgInBhdGllbnRfaWQiKSwgc2lnbmF0dXJlX2xhYmVscywgcmVwb3J0X2NvdW50ID0gRkFMU0UpIHsKICBwcm9wcyA8LSBzdWJzZXQoeCwgc2VsZWN0PWMoYnksIGNvbG5hbWVzKHgpW2NvbG5hbWVzKHgpICVpbiUgc2lnbmF0dXJlX2xhYmVsc10pKSAlPiUgZ3JvdXBfYnlfKC5kb3RzPWJ5KSAlPiUgc3VtbWFyaXNlX2VhY2goZnVucyhzdW0pKQogIAogIGlmIChyZXBvcnRfY291bnQpIHsKICAgIGNvdW50cyA8LSBzdWJzZXQoeCwgc2VsZWN0PWMoYnksIGNvbG5hbWVzKHgpW2NvbG5hbWVzKHgpICVpbiUgc2lnbmF0dXJlX2xhYmVsc10pKSAlPiUgZ3JvdXBfYnlfKC5kb3RzPWJ5KSAlPiUgc3VtbWFyaXNlKG49bigpKQogICAgcHJvcHMgPC0gcGx5cjo6am9pbihwcm9wcyAlPiUgYXMuZGF0YS5mcmFtZSwgY291bnRzICU+JSBhcy5kYXRhLmZyYW1lKQogIH0KICBzdW1zIDwtIHJvd1N1bXMoc3Vic2V0KHByb3BzLCBzZWxlY3Q9Y29sbmFtZXMocHJvcHNbY29sbmFtZXMocHJvcHMpICVpbiUgc2lnbmF0dXJlX2xhYmVsc10pKSkKICBzaWdzIDwtIGludGVyc2VjdChzaWduYXR1cmVfbGFiZWxzLCBjb2xuYW1lcyhwcm9wcykpCiAgZm9yIChzaWcgaW4gc2lncykgewogICAgcHJvcHNbLHNpZ10gPC0gcHJvcHNbLHNpZ10vc3VtcwogIH0KICByZXR1cm4ocHJvcHMpCn0KYGBgCgojIyMgU2lnbmF0dXJlcwoKIVtdKGByIG1tY3RtX2ZpbmFsX3BhdGllbnRfc2lncGxvdGApCgojIyMgU2FtcGxlIHNpZ25hdHVyZSBwcm9wb3J0aW9ucwoKVGhlc2UgcHJvcG9ydGlvbnMgd29uJ3QgYmUgZXhhY3RseSBlcXVhbCB0byB0aGUgdG9waWMgcHJvcG9ydGlvbnMgdGhhdCB0aGUgbW9kZWwgb3V0cHV0cyAodGhleSBhcmUgdmFyaWF0aW9uYWwgZXN0aW1hdGVzKSwgYnV0IHdlJ3JlIGFjdHVhbGx5IGRvaW5nIHByZXR0eSBkYXJuIHdlbGwuIAoKVE9ETzogTWFrZSBhIFFDIHBsb3QuIEZyb20gYSBnbGFuY2UgaXQgc2VlbXMgdGhhdCBwcm9wb3J0aW9uIGVycm9yIGlzIHVzdWFsbHkgd2l0aGluIDEtNSUgcmVsYXRpdmUgZXJyb3IuIAoKYGBge3J9CnNhbXBsZV9wcm9wc19zbnYgPC0gc3VtbWFyaXplX3NpZ25hdHVyZV9wcm9wb3J0aW9ucyhzaWdfcmVzdWx0c19zbnYsIGJ5PWMoImNvbmRlbnNlZF9pZCIsICJwYXRpZW50X2lkIiksIHNpZ25hdHVyZV9sYWJlbHMpCnNhbXBsZV9wcm9wc19zdiA8LSBzdW1tYXJpemVfc2lnbmF0dXJlX3Byb3BvcnRpb25zKHNpZ19yZXN1bHRzX3N2LCBieT1jKCJjb25kZW5zZWRfaWQiLCAicGF0aWVudF9pZCIpLCBzaWduYXR1cmVfbGFiZWxzKQoKcGF0aWVudF9wcm9wc19zbnYgPC0gc2lnX3Jlc3VsdHNfc252ICU+JSBzdWJzZXQoc2VsZWN0PWMoInBhdGllbnRfaWQiLCAiY2hyb20iLCAiY29vcmQiLCAicmVmIiwgImFsdCIsIGludGVyc2VjdChzaWduYXR1cmVfbGFiZWxzLCBjb2xuYW1lcyhzaWdfcmVzdWx0c19zbnYpKSkpICU+JSB1bmlxdWUgJT4lIHN1bW1hcml6ZV9zaWduYXR1cmVfcHJvcG9ydGlvbnMoYnk9YygicGF0aWVudF9pZCIpLCBzaWduYXR1cmVfbGFiZWxzKQpwYXRpZW50X3Byb3BzX3N2IDwtIHNpZ19yZXN1bHRzX3N2ICU+JSBzdWJzZXQoc2VsZWN0PWMoInBhdGllbnRfaWQiLCAicHJlZGljdGlvbl9pZCIsIGludGVyc2VjdChzaWduYXR1cmVfbGFiZWxzLCBjb2xuYW1lcyhzaWdfcmVzdWx0c19zdikpKSkgJT4lIHVuaXF1ZSAlPiUgc3VtbWFyaXplX3NpZ25hdHVyZV9wcm9wb3J0aW9ucyhieT1jKCJwYXRpZW50X2lkIiksIHNpZ25hdHVyZV9sYWJlbHMpCmBgYAoKYGBge3J9CnNhbXBsZV9wcm9wcyA8LSBwbHlyOjpqb2luKHNhbXBsZV9wcm9wc19zbnYgJT4lIGFzLmRhdGEuZnJhbWUsIHNhbXBsZV9wcm9wc19zdiAlPiUgYXMuZGF0YS5mcmFtZSkKc2FtcGxlX3Byb3BzIDwtIHJiaW5kLmZpbGwoc2FtcGxlX3Byb3BzLCBzaWdfcmVzdWx0c19ub25pdGgpCgpzYW1wbGVfcHJvcHNfbWF0IDwtIHNhbXBsZV9wcm9wcyAlPiUgY29sdW1uX3RvX3Jvd25hbWVzKCJjb25kZW5zZWRfaWQiKSAlPiUgc3Vic2V0KHNlbGVjdD1jKGNvbG5hbWVzKHNhbXBsZV9wcm9wcylbY29sbmFtZXMoc2FtcGxlX3Byb3BzKSAlaW4lIHNpZ25hdHVyZV9sYWJlbHNdKSkKCnNhbXBsZV9wcm9wc19tYXRfc2NhbGVkIDwtIHNjYWxlKHNhbXBsZV9wcm9wc19tYXQpCiNbLGMoIlNOVi0yIiwgIlNOVi01IiwgIlNWLTMiLCAiU1YtNiIsICJTVi04IiwgIlNWLTEiLCAiU1YtNyIsICJTVi01IiwgIlNOVi0zIildCnNhbXBsZV9wcm9wc19oZWF0IDwtIHBoZWF0bWFwKHNhbXBsZV9wcm9wc19tYXRfc2NhbGVkLCBmb250c2l6ZV9yb3cgPSA1LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbHVzdGVyaW5nX21ldGhvZCA9ICJ3YXJkLkQyIikKYGBgCgpgYGB7cn0KcGF0aWVudF9wcm9wcyA8LSBwbHlyOjpqb2luKHBhdGllbnRfcHJvcHNfc252ICU+JSBhcy5kYXRhLmZyYW1lLCBwYXRpZW50X3Byb3BzX3N2ICU+JSBhcy5kYXRhLmZyYW1lKQpwYXRpZW50X3Byb3BzIDwtIHJiaW5kLmZpbGwocGF0aWVudF9wcm9wcywgc2lnX3Jlc3VsdHNfbm9uaXRoICU+JSBwbHlyOjpyZW5hbWUoYygiY29uZGVuc2VkX2lkIj0icGF0aWVudF9pZCIpKSkKCnBhdGllbnRfcHJvcHNfbWF0IDwtIHBhdGllbnRfcHJvcHMgJT4lIGNvbHVtbl90b19yb3duYW1lcygicGF0aWVudF9pZCIpICU+JSBzdWJzZXQoc2VsZWN0PWMoY29sbmFtZXMocGF0aWVudF9wcm9wcylbY29sbmFtZXMocGF0aWVudF9wcm9wcykgJWluJSBzaWduYXR1cmVfbGFiZWxzXSkpCgpwYXRpZW50X3Byb3BzX21hdF9zY2FsZWQgPC0gc2NhbGUocGF0aWVudF9wcm9wc19tYXQpCiNbLGMoIlNOVi0yIiwgIlNOVi01IiwgIlNWLTMiLCAiU1YtNiIsICJTVi04IiwgIlNWLTEiLCAiU1YtNyIsICJTVi01IiwgIlNOVi0zIildCnBhdGllbnRfcHJvcHNfaGVhdCA8LSBwaGVhdG1hcChwYXRpZW50X3Byb3BzX21hdF9zY2FsZWQsIGZvbnRzaXplX3JvdyA9IDUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJpbmdfbWV0aG9kID0gIndhcmQuRDIiLCBjbHVzdGVyaW5nX2Rpc3RhbmNlX3Jvd3MgPSAiY29ycmVsYXRpb24iKQpgYGAKCiMjIyMgSVRIIGNvaG9ydAoKYGBge3J9CnNhbXBsZV9jbHVzdHMgPC0gYXMuZGF0YS5mcmFtZShjdXRyZWUoc2FtcGxlX3Byb3BzX2hlYXQkdHJlZV9yb3csIDQpKSAlPiUgc2V0TmFtZXMoYygiY2x1c3RlciIpKSAlPiUgcm93bmFtZXNfdG9fY29sdW1uKCJjb25kZW5zZWRfaWQiKQoKaWhjX3N0YWJpbGl6ZV9zdWJzZXQgPC0gc3RhYmlsaXplX2loY192YXJpYW5jZXMoaWhjX3RhYmxlX3NsaWRlLCBpaGNfdGFibGUsIHRpbHR5cGVzKQoKZGF0YV9tYXRyaWNlcyA8LSBsaXN0KHNhbXBsZV9jbHVzdHMsIGloY19zdGFiaWxpemVfc3Vic2V0LCB4Y3JfZGl2ZXJzaXR5LCBjZWxsdHlwZV9kZiwgcGF0aHdheV9kZiwgcHJvcG9ydGlvbl9zdWJjbG9uYWxpdHlfc3Vic2V0KQpkYXRhX21hdHJpY2VzIDwtIGxhcHBseShkYXRhX21hdHJpY2VzLCBmdW5jdGlvbih4KSB7CiAgeCAlPiUgZHBseXI6OnNlbGVjdCgtb25lX29mKCJwYXRpZW50X2lkIikpCn0pCgpjb21iaW5lZF9kZiA8LSBSZWR1Y2UoZnVuY3Rpb24oeCwgeSkgcGx5cjo6am9pbih4LHksdHlwZT0nZnVsbCcpLCBkYXRhX21hdHJpY2VzKQpjb21iaW5lZF9kZiA8LSBzdWJzZXQoY29tYmluZWRfZGYsICFpcy5uYShjbHVzdGVyKSAmICFjb25kZW5zZWRfaWQgJWluJSBjKCI3X0Jybk0iKSkKY29tYmluZWRfZGYgPC0gc3Vic2V0KGNvbWJpbmVkX2RmLCBjb25kZW5zZWRfaWQgJWluJSBzdWJzZXQoc2FtcGxlcywgcHJvamVjdF9jb2RlID09ICJJVEgiKSRjb25kZW5zZWRfaWQpCgpjb21iaW5lZF9tYXQgPC0gc3Vic2V0KGNvbWJpbmVkX2RmLCBzZWxlY3Q9LWMoY29uZGVuc2VkX2lkLCBjbHVzdGVyKSkKcm93bmFtZXMoY29tYmluZWRfbWF0KSA8LSBjb21iaW5lZF9kZiRjb25kZW5zZWRfaWQKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9OH0KI3JlZl9kZW5kcm9ncmFtIDwtIHBydW5lKGFzLmRlbmRyb2dyYW0oc2FtcGxlX2hlYXQkdHJlZV9yb3cpLCAiN19Ccm5NIikKCnNhbXBsZV9vcmRlciA8LSBzYW1wbGVfcHJvcHNfaGVhdCR0cmVlX3JvdyRsYWJlbHNbc2FtcGxlX3Byb3BzX2hlYXQkdHJlZV9yb3ckb3JkZXJdCnNhbXBsZV9vcmRlciA8LSBpbnRlcnNlY3Qoc2FtcGxlX29yZGVyLCByb3duYW1lcyhjb21iaW5lZF9tYXQpKQoKY2x1c3Rlcl9hbm5vdGF0aW9ucyA8LSBzdWJzZXQoY29tYmluZWRfZGYsIHNlbGVjdD1jKGNvbmRlbnNlZF9pZCwgY2x1c3RlcikpCgpTSUdTIDwtIGMoIlNWLTMiLCAiU05WLTUiLCAiU1YtNiIsICJTVi04IiwgIlNOVi0yIikKc2lnX2Fubm90YXRpb25zIDwtIHJvd25hbWVzX3RvX2NvbHVtbihkYXRhLmZyYW1lKHNhbXBsZV9wcm9wc19tYXRbLFNJR1NdLCBjaGVjay5uYW1lcyA9IEZBTFNFKSwgdmFyID0gImNvbmRlbnNlZF9pZCIpCgojIyBUYWtlIGxvZ2FyaXRobXMgdG8gfnZhcmlhbmNlIHN0YWJpbGl6ZQp2YXJpYW50X2NvdW50X2Fubm90YXRpb25zIDwtIHZhcmlhbnRfc2FtcGxlICU+JSBncm91cF9ieV8oLmRvdHM9ImNvbmRlbnNlZF9pZCIpICU+JSAKICBzdW1tYXJpc2Uoc252X2NvdW50PWxvZyhzdW0oc252X2NvdW50KSksIGJrcHRfY291bnQ9bG9nKHN1bShia3B0X2NvdW50KSkpICU+JQogIHN1YnNldChzZWxlY3Q9YygiY29uZGVuc2VkX2lkIiwgInNudl9jb3VudCIsICJia3B0X2NvdW50IikpCgpyb3dfYW5ub3RhdGlvbnMgPC0gUmVkdWNlKHBseXI6OmpvaW4sIGxpc3QoY2x1c3Rlcl9hbm5vdGF0aW9ucywgbW9sc3VidHlwZXMsIHNpZ19hbm5vdGF0aW9ucywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhcmlhbnRfY291bnRfYW5ub3RhdGlvbnMpKQpyb3dfYW5ub3RhdGlvbnMgPC0gcm93X2Fubm90YXRpb25zICU+JSBjb2x1bW5fdG9fcm93bmFtZXModmFyID0gImNvbmRlbnNlZF9pZCIpCnJvd19hbm5vdGF0aW9uc19pdGggPC0gcm93X2Fubm90YXRpb25zCgpjb21iaW5lZF9tYXRfc2NhbGVkIDwtIHNjYWxlKGNvbWJpbmVkX21hdCkKCnBoZWF0bWFwKGNsaXBfdmFsdWVzKGNvbWJpbmVkX21hdF9zY2FsZWQsIDIsIC0yKVtzYW1wbGVfb3JkZXIsXSwgY2x1c3Rlcl9yb3dzID0gRkFMU0UsIGNsdXN0ZXJfY29scyA9IFRSVUUsIGFubm90YXRpb25fcm93ID0gcm93X2Fubm90YXRpb25zLCBmb250c2l6ZSA9IDYpCgpjb21iaW5lZF9tYXRfc2NhbGVkX2l0aCA8LSBjb21iaW5lZF9tYXRfc2NhbGVkCmBgYAoKIyMjIyBJQ0dDIHZhbGlkYXRpb24KCmBgYHtyfQppY2djX2ZiaV9zdGF0dXMgPC0gZnJlYWQod2FuZ19pY2djX2ZiaV9zdGF0dXNfZmlsZSkKY29sbmFtZXMoaWNnY19mYmlfc3RhdHVzKSA8LSBtYXB2YWx1ZXMoY29sbmFtZXMoaWNnY19mYmlfc3RhdHVzKSwgZnJvbSA9IGMoIkNhc2VfSUQiKSwgdG8gPSBjKCJjb25kZW5zZWRfaWQiKSkKYGBgCgpgYGB7cn0KY29sbmFtZXMoaWNnY19pbW11bmUpIDwtIG1hcHZhbHVlcyhjb2xuYW1lcyhpY2djX2ltbXVuZSksICJuZXdfaWQiLCAiY29uZGVuc2VkX2lkIikKCmRhdGFfbWF0cmljZXMgPC0gbGlzdChzYW1wbGVfY2x1c3RzLCBpY2djX2ltbXVuZSkKZGF0YV9tYXRyaWNlcyA8LSBsYXBwbHkoZGF0YV9tYXRyaWNlcywgZnVuY3Rpb24oeCkgewogIHggJT4lIGRwbHlyOjpzZWxlY3QoLW9uZV9vZigicGF0aWVudF9pZCIpKQp9KQoKY29tYmluZWRfZGYgPC0gUmVkdWNlKGZ1bmN0aW9uKHgsIHkpIHBseXI6OmpvaW4oeCx5LHR5cGU9J2lubmVyJyksIGRhdGFfbWF0cmljZXMpCiNjb21iaW5lZF9kZiA8LSBzdWJzZXQoY29tYmluZWRfZGYsIGNsdXN0ZXIgPT0gMSkKCmNvbWJpbmVkX21hdCA8LSBzdWJzZXQoY29tYmluZWRfZGYsIHNlbGVjdD0tYyhjb25kZW5zZWRfaWQsIGNsdXN0ZXIpKQpyb3duYW1lcyhjb21iaW5lZF9tYXQpIDwtIGNvbWJpbmVkX2RmJGNvbmRlbnNlZF9pZApgYGAKCmBgYHtyLCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD04fQpzYW1wbGVfb3JkZXIgPC0gc2FtcGxlX3Byb3BzX2hlYXQkdHJlZV9yb3ckbGFiZWxzW3NhbXBsZV9wcm9wc19oZWF0JHRyZWVfcm93JG9yZGVyXQpzYW1wbGVfb3JkZXIgPC0gaW50ZXJzZWN0KHNhbXBsZV9vcmRlciwgcm93bmFtZXMoY29tYmluZWRfbWF0KSkKCmNsdXN0ZXJfYW5ub3RhdGlvbnMgPC0gc3Vic2V0KGNvbWJpbmVkX2RmLCBzZWxlY3Q9Yyhjb25kZW5zZWRfaWQsIGNsdXN0ZXIpKQoKU0lHUyA8LSBjKCJTVi0zIiwgIlNOVi01IiwgIlNWLTYiLCAiU1YtOCIsICJTTlYtMiIpCnNpZ19hbm5vdGF0aW9ucyA8LSByb3duYW1lc190b19jb2x1bW4oZGF0YS5mcmFtZShzYW1wbGVfcHJvcHNfbWF0WyxTSUdTXSwgY2hlY2submFtZXMgPSBGQUxTRSksIHZhciA9ICJjb25kZW5zZWRfaWQiKQoKc3VidHlwZV9hbm5vdGF0aW9ucyA8LSBzdWJzZXQoaWNnY19zdWJ0eXBlcywgc2VsZWN0PWMoImljZ2NfZG9ub3JfaWQiLCAic3VidHlwZSIsICJubWZfc3VidHlwZSIpKQpjb2xuYW1lcyhzdWJ0eXBlX2Fubm90YXRpb25zKVsxXSA8LSAiY29uZGVuc2VkX2lkIgoKaWNnY19mYmlfYW5ub3RhdGlvbnMgPC0gc3Vic2V0KGljZ2NfZmJpX3N0YXR1cywgc2VsZWN0PWMoImNvbmRlbnNlZF9pZCIsICJQYXRjaCBldCBhbC4gQ2xhc3MiLCAiUGF0Y2ggZXQgYWwuIE1vbGVjdWxhciBTdWJncm91cCIsICJCUkNBLnN0YXR1cyIsICJTdWJncm91cCIpKQoKcm93X2Fubm90YXRpb25zIDwtIFJlZHVjZShwbHlyOjpqb2luLCBsaXN0KGNsdXN0ZXJfYW5ub3RhdGlvbnMsIHNpZ19hbm5vdGF0aW9ucywgc3VidHlwZV9hbm5vdGF0aW9ucywgaWNnY19mYmlfYW5ub3RhdGlvbnMpKQpyb3dfYW5ub3RhdGlvbnMgPC0gcm93X2Fubm90YXRpb25zICU+JSBjb2x1bW5fdG9fcm93bmFtZXModmFyID0gImNvbmRlbnNlZF9pZCIpCnJvd19hbm5vdGF0aW9uc19ub2l0aCA8LSByb3dfYW5ub3RhdGlvbnMKCiNzZWxlY3Rfcm93cyA8LSByb3duYW1lcyhzdWJzZXQocm93X2Fubm90YXRpb25zLCBgU1YtNGAgPCAxKSkKbm90eCA8LSBzdWJzZXQoc3BlY2ltZW5fZGF0YSwgc3RyX2RldGVjdChzcGVjaW1lbl90eXBlLCAiUHJpbWFyeSIpICYgc3BlY2ltZW5fZG9ub3JfdHJlYXRtZW50X3R5cGUgPT0gIm5vIHRyZWF0bWVudCIpJGljZ2NfZG9ub3JfaWQKI3NhbXBsZV9vcmRlciA8LSBpbnRlcnNlY3Qoc2FtcGxlX29yZGVyLCBzZWxlY3Rfcm93cykKc2FtcGxlX29yZGVyIDwtIGludGVyc2VjdChzYW1wbGVfb3JkZXIsIG5vdHgpCgojb3JkZXJfZmJpIDwtIG9yZGVyKHJvd19hbm5vdGF0aW9uc1tyb3duYW1lcyhjb21iaW5lZF9tYXRfc2NhbGVkKSxdJGBTVi04YCkKCiNjb21iaW5lZF9tYXRfc2NhbGVkIDwtIHNjYWxlKGNvbWJpbmVkX21hdCkKY29tYmluZWRfbWF0X3NjYWxlZCA8LSBhcy5kYXRhLmZyYW1lKGFwcGx5KGNvbWJpbmVkX21hdCwgMiwgZnVuY3Rpb24oeCkgKHgtbWVkaWFuKHgsbmEucm09VFJVRSkpL21hZCh4LG5hLnJtPVRSVUUpKSkKCmNvbWJpbmVkX21hdF9zY2FsZWQgPC0gY29tYmluZWRfbWF0X3NjYWxlZFtzYW1wbGVfb3JkZXIsXQpvdGhlcl9tYXQgPC0gc2FtcGxlX3Byb3BzX21hdF9zY2FsZWRbc2FtcGxlX29yZGVyLF0KCnBoZWF0bWFwKGNsaXBfdmFsdWVzKGNiaW5kKGNvbWJpbmVkX21hdF9zY2FsZWQsIG90aGVyX21hdCksIDIsIC0yKSwgY2x1c3Rlcl9yb3dzID0gRkFMU0UsIGNsdXN0ZXJfY29scyA9IFRSVUUsIGFubm90YXRpb25fcm93ID0gcm93X2Fubm90YXRpb25zLCBmb250c2l6ZSA9IDYsIGNsdXN0ZXJpbmdfZGlzdGFuY2VfY29scyA9ICJjb3JyZWxhdGlvbiIsIGNsdXN0ZXJpbmdfbWV0aG9kID0gIndhcmQuRDIiKQoKY29tYmluZWRfbWF0X3NjYWxlZF9ub2l0aCA8LSBjb21iaW5lZF9tYXRfc2NhbGVkCmBgYAoKCmBgYHtyfQppY2djX2NsaW5pY2FsX2xhYmVsZWQgPC0gUmVkdWNlKGZ1bmN0aW9uKHgseSkgcGx5cjo6am9pbih4LHksdHlwZT0nZnVsbCcpLCBsaXN0KHNldE5hbWVzKGNsdXN0ZXJfYW5ub3RhdGlvbnMsIGMoImljZ2NfZG9ub3JfaWQiLCAiY2x1c3RlciIpKSwgaWNnY19jbGluaWNhbCwgc2V0TmFtZXMoc3Vic2V0KGljZ2NfZmJpX2Fubm90YXRpb25zLCBzZWxlY3Q9YygiY29uZGVuc2VkX2lkIiwgIkJSQ0Euc3RhdHVzIiwgIlN1Ymdyb3VwIikpLCBjKCJpY2djX2Rvbm9yX2lkIiwgIkJSQ0Euc3RhdHVzIiwgIldhbmdfc3ViZ3JvdXAiKSkpKQoKaWNnY19jbGluaWNhbF9sYWJlbGVkJFN1cnZPYmogPC0gd2l0aChpY2djX2NsaW5pY2FsX2xhYmVsZWQsIFN1cnYoZG9ub3Jfc3Vydml2YWxfdGltZSwgZG9ub3Jfdml0YWxfc3RhdHVzID09ICJkZWNlYXNlZCIpKQpgYGAKClN1cnZpdmFsIGJ5IEZCSSBzdGF0dXMgLi4uCgpgYGB7cn0Kc2ltcGxlX3N1cnZpdmFsX2FuYWx5c2lzKFN1cnZPYmogfiBjbHVzdGVyICE9IDEsIGRhdGEgPSBzdWJzZXQoaWNnY19jbGluaWNhbF9sYWJlbGVkLCBjbHVzdGVyICE9IDQpKQpgYGAKCmBgYHtyfQpzaW1wbGVfc3Vydml2YWxfYW5hbHlzaXMoU3Vydk9iaiB+IGNsdXN0ZXIsIGRhdGEgPSBzdWJzZXQoaWNnY19jbGluaWNhbF9sYWJlbGVkLCBjbHVzdGVyICE9IDQpKQpgYGAKCmBgYHtyfQpzaW1wbGVfc3Vydml2YWxfYW5hbHlzaXMoU3Vydk9iaiB+IFdhbmdfc3ViZ3JvdXAsIGRhdGEgPSBpY2djX2NsaW5pY2FsX2xhYmVsZWQpCmBgYAoKIyMjIyBDb21iaW5lZAoKIyMjIyMgU2FtcGxlLWxldmVsCgpgYGB7cn0KY29tYmluZWRfbWF0X3NjYWxlZF9hbGwgPC0gcmJpbmQuZmlsbChjb21iaW5lZF9tYXRfc2NhbGVkX2l0aCAlPiUgYXMuZGF0YS5mcmFtZSAlPiUgcm93bmFtZXNfdG9fY29sdW1uKCJjb25kZW5zZWRfaWQiKSwgY29tYmluZWRfbWF0X3NjYWxlZF9ub2l0aCAlPiUgYXMuZGF0YS5mcmFtZSAlPiUgcm93bmFtZXNfdG9fY29sdW1uKCJjb25kZW5zZWRfaWQiKSkgJT4lIGNvbHVtbl90b19yb3duYW1lcygiY29uZGVuc2VkX2lkIikKCnJvd19hbm5vdGF0aW9uc19hbGwgPC0gcmJpbmQuZmlsbChyb3dfYW5ub3RhdGlvbnNfaXRoICU+JSByb3duYW1lc190b19jb2x1bW4oImNvbmRlbnNlZF9pZCIpLCByb3dfYW5ub3RhdGlvbnNfbm9pdGggJT4lIHJvd25hbWVzX3RvX2NvbHVtbigiY29uZGVuc2VkX2lkIikpICU+JSBjb2x1bW5fdG9fcm93bmFtZXMoImNvbmRlbnNlZF9pZCIpCgpzYW1wbGVfb3JkZXIgPC0gc2FtcGxlX3Byb3BzX2hlYXQkdHJlZV9yb3ckbGFiZWxzW3NhbXBsZV9wcm9wc19oZWF0JHRyZWVfcm93JG9yZGVyXQpzYW1wbGVfb3JkZXIgPC0gaW50ZXJzZWN0KHNhbXBsZV9vcmRlciwgcm93bmFtZXMoY29tYmluZWRfbWF0X3NjYWxlZF9hbGwpKQoKI2NvbWJpbmVkX21hdF9zY2FsZWQgPC0gYXMuZGF0YS5mcmFtZShhcHBseShjb21iaW5lZF9tYXRfYWxsLCAyLCBmdW5jdGlvbih4KSAoeC1tZWRpYW4oeCxuYS5ybT1UUlVFKSkvbWFkKHgsbmEucm09VFJVRSkpKQpjb21iaW5lZF9tYXRfc2NhbGVkIDwtIGNvbWJpbmVkX21hdF9zY2FsZWRfYWxsW3NhbXBsZV9vcmRlcixdCgpuYW5vc3RyaW5nX3ZhcnMgPC0gcm93bmFtZXMoaWNnY19jZWxsdHlwZV9tYXRyaXgpCmNvbWJpbmVkX21hdF9zY2FsZWRfc3Vic2V0IDwtIHN1YnNldChjb21iaW5lZF9tYXRfc2NhbGVkLCBzZWxlY3Q9bmFub3N0cmluZ192YXJzKQpjb21iaW5lZF9tYXRfc2NhbGVkX3N1YnNldCA8LSBjb21iaW5lZF9tYXRfc2NhbGVkX3N1YnNldFshYXBwbHkoY29tYmluZWRfbWF0X3NjYWxlZF9zdWJzZXQsIDEsIGZ1bmN0aW9uKHgpIGFsbChpcy5uYSh4KSkpLF0KYGBgCgpgYGB7ciwgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9MTJ9CnBoZWF0bWFwKGNsaXBfdmFsdWVzKGNvbWJpbmVkX21hdF9zY2FsZWRfc3Vic2V0LCAyLCAtMiksIGNsdXN0ZXJfcm93cyA9IEZBTFNFLCBjbHVzdGVyX2NvbHMgPSBUUlVFLCBhbm5vdGF0aW9uX3JvdyA9IHJvd19hbm5vdGF0aW9uc19hbGwsIGZvbnRzaXplID0gNiwgY2x1c3RlcmluZ19kaXN0YW5jZV9jb2xzID0gImNvcnJlbGF0aW9uIiwgY2x1c3RlcmluZ19tZXRob2QgPSAid2FyZC5EMiIpCmBgYAoKRG9lc24ndCByZWFsbHkgY2x1c3RlciBjb25zaXN0ZW50bHkgYmV0d2VlbiBJQ0dDIGFuZCBvdXIgc2FtcGxlcy4gCgpUT0RPOiBCYXRjaCBjb3JyZWN0IElDR0MgYW5kIG91ciBkYXRhIHRvZ2V0aGVyIChJIHRoaW5rIEkgYWxyZWFkeSBkaWQgdGhpcyBzb21ld2hlcmUpIC0tIGFuZCBub3JtYWxpemUgdGhlIGZpbmFsIG1hdHJpeCBpbiBvbmUgc3RlcCByYXRoZXIgdGhhbiBub3JtYWxpemluZyBzZXBhcmF0ZWx5IGZvciBlYWNoIHN1YmNvaG9ydCBhbmQgY29tYmluaW5nIHRob3NlIHRvZ2V0aGVyLiBPdGhlcndpc2Ugd2UgY291bGQgYWx3YXlzIGJlIHN1YmplY3QgdG8gdGhlIHNjZW5hcmlvIHdoZXJlIHRoZSBJVEggY29ob3J0IG1heSBiZSBza2V3ZWQgaW4gaW1tdW5lIHJlc3BvbnNlIChlaXRoZXIgYWxsIHJlbGF0aXZlbHkgbG93IG9yIGhpZ2gpIGNvbXBhcmVkIHRvIHRoZSBJQ0dDIGNvaG9ydC4gCgpUT0RPOiBVc2UgYWJzb2x1dGUgY291bnRzIG9mIG11dGF0aW9ucyBmb3IgZWFjaCBzaWduYXR1cmUsIGFuZCBjbHVzdGVyIGJhc2VkIG9uIHRob3NlLiBJdCBtYXkgYmUgdGhhdCBzYW1wbGVzIHdpdGggdG9vIGxvdy9oaWdoIG11dGF0aW9uIGNvdW50cyBhcmUgbm90IGNsdXN0ZXJpbmcgcHJvcGVybHkuIAoKIyMjIyMgUGF0aWVudC1sZXZlbAoKYGBge3J9CiNpdGhfaWNnY19iYXRjaF9jb3JyZWN0ZWRfZXhwcmVzc2lvbl9maWxlIDwtICJ+L3NoYWhsYWIvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9yZXN1bHRzL3RhYmxlcy9ydW4yL2l0aF9pY2djX21lcmdlZF9iYy50c3YiCgppdGhfaWNnY19leHByZXNzaW9uIDwtIGZyZWFkKGl0aF9pY2djX2JhdGNoX2NvcnJlY3RlZF9leHByZXNzaW9uX2ZpbGUpCgppdGhfaWNnY19jZWxsdHlwZV9leHByIDwtIGNyZWF0ZV9jZWxsdHlwZV9tYXRyaXgoaXRoX2ljZ2NfZXhwcmVzc2lvbiwgbGFiZWxzLCBkYl9wYXRoLCBjb252ZXJ0X2lkcyA9IEZBTFNFKQppdGhfaWNnY19wYXRod2F5X2V4cHIgPC0gY3JlYXRlX3BhdGh3YXlfbWF0cml4KGl0aF9pY2djX2V4cHJlc3Npb24sIGxhYmVscywgZGJfcGF0aCwgY29udmVydF9pZHMgPSBGQUxTRSkKCmljZ2Nfc3BlY2ltZW5fZGF0YSA8LSBmcmVhZChpY2djX3NwZWNpbWVuX2ZpbGUpCmBgYAoKYGBge3J9CnN1bW1hcml6ZV9leHByZXNzaW9uX2J5X3BhdGllbnQgPC0gZnVuY3Rpb24oZXhwcikgewogIHByaW1hcnlfc3BlY2ltZW5fZGF0IDwtIHN1YnNldChpY2djX3NwZWNpbWVuX2RhdGEsIHN0cl9kZXRlY3Qoc3BlY2ltZW5fdHlwZSwgIlByaW1hcnkiKSkKICAKICBub3R4IDwtIHN1YnNldChwcmltYXJ5X3NwZWNpbWVuX2RhdCwgc3RyX2RldGVjdChzcGVjaW1lbl90eXBlLCAiUHJpbWFyeSIpICYgc3BlY2ltZW5fZG9ub3JfdHJlYXRtZW50X3R5cGUgPT0gIm5vIHRyZWF0bWVudCIpJGljZ2NfZG9ub3JfaWQKICBwcmltYXJ5X3NwZWNpbWVuX2RhdCA8LSBzdWJzZXQocHJpbWFyeV9zcGVjaW1lbl9kYXQsIGljZ2NfZG9ub3JfaWQgJWluJSBub3R4KQogIAogIHggPC0gc2V0TmFtZXMobWVsdChhcy5tYXRyaXgoZXhwcikpLCBjKCJOYW1lIiwgInNhbXBsZSIsICJleHByIikpCiAgeCRwYXRpZW50X2lkIDwtIG1hcF9pZChhcy5jaGFyYWN0ZXIoeCRzYW1wbGUpLCBmcm9tID0gImNvbmRlbnNlZF9pZCIsIHRvPSJwYXRpZW50X2lkIiwgZGJfcGF0aCkKICBpZHggPC0gaXMubmEoeCRwYXRpZW50X2lkKQogIGRvbm9yX2xhYmVscyA8LSBkZl9hc19tYXAocHJpbWFyeV9zcGVjaW1lbl9kYXQsIGFzLmNoYXJhY3Rlcih4JHNhbXBsZVtpZHhdKSwgZnJvbSA9ICJpY2djX3NwZWNpbWVuX2lkIix0byA9ICJpY2djX2Rvbm9yX2lkIikKICB4JHBhdGllbnRfaWRbaWR4XSA8LSBkb25vcl9sYWJlbHMKICB4IDwtIHN1YnNldCh4LCAhaXMubmEocGF0aWVudF9pZCkpCiAgeF9zdW0gPC0geCAlPiUgZ3JvdXBfYnkoTmFtZSwgcGF0aWVudF9pZCkgJT4lIHN1bW1hcmlzZShleHByPW1lYW4oZXhwciwgbmEucm09VFJVRSkpCiAgCiAgcmVzIDwtIGRjYXN0KHhfc3VtLCBmb3JtdWxhID0gTmFtZSB+IHBhdGllbnRfaWQsIHZhbHVlLnZhciA9ICJleHByIikKICByZXR1cm4ocmVzKQp9CmBgYAoKYGBge3J9Ck5DTFVTVCA8LSAzCmNsdXN0ZXJzIDwtIGN1dHJlZShwYXRpZW50X3Byb3BzX2hlYXQkdHJlZV9yb3csIE5DTFVTVCkKcGF0aWVudF9jbHVzdHMgPC0gbWFrZV9jbHVzdGVyX2ZyYW1lKGNsdXN0ZXJzKQoKY2x1c3Rlcl9hbm5vdGF0aW9ucyA8LSBzdWJzZXQocGx5cjo6cmVuYW1lKHBhdGllbnRfY2x1c3RzLCBjKCduZXdfaWQnPSdwYXRpZW50X2lkJykpLCBzZWxlY3Q9YyhwYXRpZW50X2lkLCBjbHVzdGVyKSkKCml0aF9pY2djX2NlbGx0eXBlX2V4cHJfcGF0aWVudCA8LSBzdW1tYXJpemVfZXhwcmVzc2lvbl9ieV9wYXRpZW50KGl0aF9pY2djX2NlbGx0eXBlX2V4cHIpCml0aF9pY2djX3BhdGh3YXlfZXhwcl9wYXRpZW50IDwtIHN1bW1hcml6ZV9leHByZXNzaW9uX2J5X3BhdGllbnQoaXRoX2ljZ2NfcGF0aHdheV9leHByKQoKZXhwcl9kYXQgPC0gaXRoX2ljZ2NfcGF0aHdheV9leHByX3BhdGllbnQKY29tYmluZWRfcGF0aWVudF9tYXQgPC0gZXhwcl9kYXQgJT4lIGFzLmRhdGEuZnJhbWUgJT4lIGNvbHVtbl90b19yb3duYW1lcyh2YXIgPSAiTmFtZSIpICU+JSAKICB0ICU+JSBhcy5kYXRhLmZyYW1lICU+JSByb3duYW1lc190b19jb2x1bW4oInBhdGllbnRfaWQiKQoKZGF0YV9tYXRyaWNlcyA8LSBsaXN0KGNsdXN0ZXJfYW5ub3RhdGlvbnMsIGNvbWJpbmVkX3BhdGllbnRfbWF0KQpkYXRhX21hdHJpY2VzIDwtIGxhcHBseShkYXRhX21hdHJpY2VzLCBmdW5jdGlvbih4KSB7CiAgeAp9KQoKY29tYmluZWRfZGYgPC0gUmVkdWNlKGZ1bmN0aW9uKHgsIHkpIHBseXI6OmpvaW4oeCx5LHR5cGU9J2lubmVyJyksIGRhdGFfbWF0cmljZXMpCmNvbWJpbmVkX21hdCA8LSBzdWJzZXQoY29tYmluZWRfZGYsIHNlbGVjdD0tYyhjbHVzdGVyKSkKcm93bmFtZXMoY29tYmluZWRfbWF0KSA8LSBOVUxMCmNvbWJpbmVkX21hdCA8LSBjb21iaW5lZF9tYXQgJT4lIGNvbHVtbl90b19yb3duYW1lcygicGF0aWVudF9pZCIpCgpTSUdTIDwtIGMoIlNWLTMiLCAiU05WLTUiLCAiU1YtNiIsICJTVi04IiwgIlNOVi0yIikKc2lnX2Fubm90YXRpb25zIDwtIHJvd25hbWVzX3RvX2NvbHVtbihkYXRhLmZyYW1lKHBhdGllbnRfcHJvcHNfbWF0WyxTSUdTXSwgY2hlY2submFtZXMgPSBGQUxTRSksIHZhciA9ICJwYXRpZW50X2lkIikKCnN1YnR5cGVfYW5ub3RhdGlvbnMgPC0gc3Vic2V0KGljZ2Nfc3VidHlwZXMsIHNlbGVjdD1jKCJpY2djX2Rvbm9yX2lkIiwgInN1YnR5cGUiLCAibm1mX3N1YnR5cGUiKSkKY29sbmFtZXMoc3VidHlwZV9hbm5vdGF0aW9ucylbMV0gPC0gInBhdGllbnRfaWQiCgppY2djX2ZiaV9zdGF0dXNfcGF0aWVudCA8LSBmcmVhZCh3YW5nX2ljZ2NfZmJpX3N0YXR1c19maWxlKQpjb2xuYW1lcyhpY2djX2ZiaV9zdGF0dXNfcGF0aWVudCkgPC0gbWFwdmFsdWVzKGNvbG5hbWVzKGljZ2NfZmJpX3N0YXR1c19wYXRpZW50KSwgZnJvbSA9IGMoIkNhc2VfSUQiKSwgdG8gPSBjKCJwYXRpZW50X2lkIikpCgppY2djX2ZiaV9hbm5vdGF0aW9ucyA8LSBzdWJzZXQoaWNnY19mYmlfc3RhdHVzX3BhdGllbnQsIHNlbGVjdD1jKCJwYXRpZW50X2lkIiwgIlBhdGNoIGV0IGFsLiBDbGFzcyIsICJQYXRjaCBldCBhbC4gTW9sZWN1bGFyIFN1Ymdyb3VwIiwgIkJSQ0Euc3RhdHVzIiwgIlN1Ymdyb3VwIikpCgpyb3dfYW5ub3RhdGlvbnMgPC0gUmVkdWNlKHBseXI6OmpvaW4sIGxpc3QoY2x1c3Rlcl9hbm5vdGF0aW9ucywgc2lnX2Fubm90YXRpb25zLCBzdWJ0eXBlX2Fubm90YXRpb25zLCBpY2djX2ZiaV9hbm5vdGF0aW9ucykpCgpyb3dfYW5ub3RhdGlvbnMgPC0gcm93X2Fubm90YXRpb25zICU+JSBjb2x1bW5fdG9fcm93bmFtZXMoInBhdGllbnRfaWQiKQoKY29tYmluZWRfcGF0aWVudF9tYXRfc2NhbGVkIDwtIGNvbWJpbmVkX21hdCAlPiUgc2NhbGUKCnBhdGllbnRfb3JkZXIgPC0gcGF0aWVudF9wcm9wc19oZWF0JHRyZWVfcm93JGxhYmVsc1twYXRpZW50X3Byb3BzX2hlYXQkdHJlZV9yb3ckb3JkZXJdCnBhdGllbnRfb3JkZXIgPC0gaW50ZXJzZWN0KHBhdGllbnRfb3JkZXIsIHJvd25hbWVzKGNvbWJpbmVkX3BhdGllbnRfbWF0X3NjYWxlZCkpCmBgYAoKYGBge3IsIGZpZy53aWR0aD03LCBmaWcuaGVpZ2h0PTEwfQpwaGVhdG1hcChjbGlwX3ZhbHVlcyhjb21iaW5lZF9wYXRpZW50X21hdF9zY2FsZWRbcGF0aWVudF9vcmRlcixdLCAyLCAtMiksIGNsdXN0ZXJfcm93cyA9IEZBTFNFLCBjbHVzdGVyX2NvbHMgPSBUUlVFLCBhbm5vdGF0aW9uX3JvdyA9IHJvd19hbm5vdGF0aW9ucywgZm9udHNpemUgPSA2LCBjbHVzdGVyaW5nX2Rpc3RhbmNlX2NvbHMgPSAiY29ycmVsYXRpb24iLCBjbHVzdGVyaW5nX21ldGhvZCA9ICJ3YXJkLkQyIikKYGBgCgoKCiMjIyBBbmNlc3RyYWwtZGVzY2VuZGFudCBzaWduYXR1cmUgcHJvcG9ydGlvbnMKCmBgYHtyfQphbmNkZXNjX3Byb3BzX3NudiA8LSB1bmlxdWUoc3Vic2V0KHNpZ19yZXN1bHRzX3Nudiwgc2VsZWN0PWMoInBhdGllbnRfaWQiLCAiaXNfYW5jZXN0cmFsIiwgImNocm9tIiwgImNvb3JkIiwgInJlZiIsICJhbHQiLCBzaWduYXR1cmVfbGFiZWxzW3NpZ25hdHVyZV9sYWJlbHMgJWluJSBjb2xuYW1lcyhzaWdfcmVzdWx0c19zbnYpXSkpKSAlPiUgc3VtbWFyaXplX3NpZ25hdHVyZV9wcm9wb3J0aW9ucyhieT1jKCJwYXRpZW50X2lkIiwgImlzX2FuY2VzdHJhbCIpLCBzaWduYXR1cmVfbGFiZWxzKQphbmNkZXNjX3Byb3BzX3N2IDwtIHVuaXF1ZShzdWJzZXQoc2lnX3Jlc3VsdHNfc3YsIHNlbGVjdD1jKCJwYXRpZW50X2lkIiwgInByZWRpY3Rpb25faWQiLCAiaXNfYW5jZXN0cmFsIiwgc2lnbmF0dXJlX2xhYmVsc1tzaWduYXR1cmVfbGFiZWxzICVpbiUgY29sbmFtZXMoc2lnX3Jlc3VsdHNfc3YpXSkpKSAlPiUgc3VtbWFyaXplX3NpZ25hdHVyZV9wcm9wb3J0aW9ucyhieT1jKCJwYXRpZW50X2lkIiwgImlzX2FuY2VzdHJhbCIpLCBzaWduYXR1cmVfbGFiZWxzKQoKYW5jZGVzY19wcm9wcyA8LSBwbHlyOjpqb2luKGFuY2Rlc2NfcHJvcHNfc252ICU+JSBhcy5kYXRhLmZyYW1lLCBhbmNkZXNjX3Byb3BzX3N2ICU+JSBhcy5kYXRhLmZyYW1lKQoKYW5jZGVzY19wcm9wc19tYXQgPC0gc3Vic2V0KGFuY2Rlc2NfcHJvcHMsIHNlbGVjdD1jb2xuYW1lcyhhbmNkZXNjX3Byb3BzKVtjb2xuYW1lcyhhbmNkZXNjX3Byb3BzKSAlaW4lIHNpZ25hdHVyZV9sYWJlbHNdKQpgYGAKCmBgYHtyfQphbmNkZXNjX3Byb3BzX3NjYWxlZCA8LSBzY2FsZShhbmNkZXNjX3Byb3BzX21hdCkKcm93bmFtZXMoYW5jZGVzY19wcm9wc19zY2FsZWQpIDwtIHdpdGgoYW5jZGVzY19wcm9wcywgcGFzdGUocGF0aWVudF9pZCwgaXNfYW5jZXN0cmFsLCBzZXA9Il8iKSkKCnBoZWF0bWFwKGFuY2Rlc2NfcHJvcHNfc2NhbGVkLCBjbHVzdGVyaW5nX2Rpc3RhbmNlX3Jvd3MgPSAiZXVjbGlkZWFuIiwgY2x1c3RlcmluZ19tZXRob2QgPSAid2FyZC5EMiIpCmBgYAoKYGBge3J9CmFuY2Rlc2NfcHJvcHNfbWVsdGVkIDwtIG1lbHQoYW5jZGVzY19wcm9wcywgaWQudmFycyA9IGMoInBhdGllbnRfaWQiLCAiaXNfYW5jZXN0cmFsIiksIG1lYXN1cmUudmFycyA9IGludGVyc2VjdChjb2xuYW1lcyhhbmNkZXNjX3Byb3BzKSwgc2lnbmF0dXJlX2xhYmVscyksIHZhcmlhYmxlLm5hbWUgPSAic2lnbmF0dXJlIiwgdmFsdWUubmFtZSA9ICJwcm9wb3J0aW9uIikKCnB2YWxzIDwtIHNldE5hbWVzKGRkcGx5KGFuY2Rlc2NfcHJvcHNfbWVsdGVkLCAuKHNpZ25hdHVyZSksIGZ1bmN0aW9uKHgpIHsKICBkZiA8LSBhcy5kYXRhLmZyYW1lKHgpCiAgY29ycmVzIDwtIHdpbGNveC50ZXN0KHByb3BvcnRpb24gfiBpc19hbmNlc3RyYWwsIGRmLCBwYWlyZWQ9VFJVRSkKICAKICBwdmFsIDwtIGNvcnJlcyRwLnZhbHVlCiAgZXEgPC0gc3Vic3RpdHV0ZShpdGFsaWMoUCk9PXAsIGxpc3QocD1mb3JtYXQocHZhbCwgZGlnaXRzPTMpKSkKICByZXR1cm4oYXMuY2hhcmFjdGVyKGFzLmV4cHJlc3Npb24oZXEpKSkKfSksIGMoInNpZ25hdHVyZSIsICJwLnZhbHVlIikpCgpnZ3Bsb3QoYW5jZGVzY19wcm9wc19tZWx0ZWQsIGFlcyh4PWZhY3Rvcihpc19hbmNlc3RyYWwpLCB5PXByb3BvcnRpb24pKSArIGdlb21fYm94cGxvdCgpICsgZmFjZXRfd3JhcCh+IHNpZ25hdHVyZSwgc2NhbGVzPSJmcmVlIikgKyBnZW9tX3RleHQoZGF0YT1wdmFscywgYWVzKHg9SW5mLCB5PUluZiwgbGFiZWw9cC52YWx1ZSksIGhqdXN0PTEuMSwgdmp1c3Q9MS41LHNpemU9MyxwYXJzZT1UUlVFKSArIAp0aGVtZV9idygpICsgdGhlbWVfUHVibGljYXRpb24oKQpgYGAKCiMjIyBPcmlnaW4gbm9kZSBzaWduYXR1cmUgcHJvcG9ydGlvbnMKCioqTm90ZSoqOiBXZSBjYW5ub3QgZ2V0IG5vZGUtc3BlY2lmaWMgcmVhcnJhbmdlbWVudCBzaWduYXR1cmVzLiAKCioqTm90ZSoqOiBUaGVzZSBub2RlIGxhYmVscyBETyBOT1QgY29ycmVzcG9uZCB0byBub2RlIGxhYmVscyB3aXRoaW4gdGhlIGNsb25lIHBoeWxvZ2VueTsgdGhleSBjb3JyZXNwb25kIHRvIG5vZGVzIHdpdGhpbiB0aGUgRG9sbG8gbW9kZWwuIAoKYGBge3J9Cm5vZGVfcHJvcHNfc252IDwtIHVuaXF1ZShzdWJzZXQoc2lnX3Jlc3VsdHNfc252LCBzZWxlY3Q9YygicGF0aWVudF9pZCIsICJvcmlnaW5fbm9kZSIsICJjaHJvbSIsICJjb29yZCIsICJyZWYiLCAiYWx0Iiwgc2lnbmF0dXJlX2xhYmVsc1tzaWduYXR1cmVfbGFiZWxzICVpbiUgY29sbmFtZXMoc2lnX3Jlc3VsdHNfc252KV0pKSkgJT4lIHN1bW1hcml6ZV9zaWduYXR1cmVfcHJvcG9ydGlvbnMoYnk9YygicGF0aWVudF9pZCIsICJvcmlnaW5fbm9kZSIpLCBzaWduYXR1cmVfbGFiZWxzLCByZXBvcnRfY291bnQgPSBUUlVFKQpgYGAKYGBge3J9Cm5vZGVfcHJvcHNfbWF0IDwtIHN1YnNldChub2RlX3Byb3BzX3Nudiwgc2VsZWN0PWNvbG5hbWVzKG5vZGVfcHJvcHNfc252KVtjb2xuYW1lcyhub2RlX3Byb3BzX3NudikgJWluJSBzaWduYXR1cmVfbGFiZWxzXSkKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9MTB9Cm5vZGVfcHJvcHNfc2NhbGVkIDwtIHNjYWxlKG5vZGVfcHJvcHNfbWF0KQpyb3duYW1lcyhub2RlX3Byb3BzX3NjYWxlZCkgPC0gd2l0aChub2RlX3Byb3BzX3NudiwgcGFzdGUocGF0aWVudF9pZCwgb3JpZ2luX25vZGUsIHNlcD0iXyIpKQoKcm93X2Fubm90YXRpb25zIDwtIHN1YnNldChub2RlX3Byb3BzX3Nudiwgc2VsZWN0PWMoInBhdGllbnRfaWQiLCAibiIpKQpyb3dfYW5ub3RhdGlvbnMkbiA8LSBsb2cyKHJvd19hbm5vdGF0aW9ucyRuKQpyb3dfYW5ub3RhdGlvbnMkcGF0aWVudF9pZCA8LSBmYWN0b3JfaWQocm93X2Fubm90YXRpb25zJHBhdGllbnRfaWQsIHR5cGUgPSAicGF0aWVudF9pZCIsIGRiX3BhdGgpCnJvd25hbWVzKHJvd19hbm5vdGF0aW9ucykgPC0gcm93bmFtZXMobm9kZV9wcm9wc19zY2FsZWQpCgpjbHVzdGVyaW5nX2NvbG91cnMgPC0gbGlzdChwYXRpZW50X2lkID0gcGFsX3BhdGllbnQpCgpwaGVhdG1hcChjbGlwX3ZhbHVlcyhub2RlX3Byb3BzX3NjYWxlZCwgMiwgLTIpLCBjbHVzdGVyaW5nX2Rpc3RhbmNlX3Jvd3MgPSAiZXVjbGlkZWFuIiwgY2x1c3RlcmluZ19tZXRob2QgPSAid2FyZC5EMiIsIGZvbnRzaXplX3JvdyA9IDUsIGFubm90YXRpb25fcm93ID0gcm93X2Fubm90YXRpb25zLCBhbm5vdGF0aW9uX2NvbG9ycyA9IGNsdXN0ZXJpbmdfY29sb3VycykKYGBgCgpMb29rcyBsaWtlIHRoZXJlIGFyZSBzaW1pbGFyIHNlbGVjdGlvbiBwcmVzc3VyZXMgYWN0aW5nIGF0IGVhY2ggcGFydCBvZiB0aGUgc2FtcGxlIHBoeWxvZ2VueSAtLSBpLmUuIG11dGF0aW9uIHNpZ25hdHVyZXMgYXJlICdyZWxhdGl2ZWx5JyBjb25zaXN0ZW50IHdpdGhpbiBwYXRpZW50cyB0aHJvdWdob3V0IHRpbWUuIAoKIyMjIENsb25hbCBwaHlsb2dlbnkgYnJhbmNoLXNwZWNpZmljIHNpZ25hdHVyZSBwcm9wb3J0aW9ucwoKTk9URTogVEhFIExBQkVMUyBPTiBUSEVTRSBUUkVFUyBNQVkgTk9UIEJFIFRIRSBTQU1FIEFTIFRIT1NFIElOIFRIRSBNQVBTQ0FQRVMgKHVnaCkuIAoKYGBge3J9CnRyZWVfYnJhbmNoX2RhdGEgPC0gcmVhZF9jbG9uZV90cmVlX2RhdGEoY2xvbmVfdHJlZV9maWxlLCBjbG9uZV9icmFuY2hfbGVuZ3RoX2ZpbGUsIGNsb25lX3ByZXZhbGVuY2VfZmlsZSwgZGJfcGF0aCkKdHJlZXMgPC0gbGFwcGx5KHRyZWVfYnJhbmNoX2RhdGEsIGZ1bmN0aW9uKHgpIHgkdHJlZSkKYnJhbmNoX2xlbmd0aHMgPC0gcmJpbmQuZmlsbChsYXBwbHkodHJlZV9icmFuY2hfZGF0YSwgZnVuY3Rpb24oeCkgeCRicmFuY2hfZGF0KSkKYGBgCgpgYGB7cn0Kc252X2NsdXN0ZXIgPC0gcmJpbmQuZmlsbChsYXBwbHkoc2VxX2Fsb25nKHNudl9jbHVzdGVyX2ZpbGVzKSwgZnVuY3Rpb24oaSkgewogIGYgPC0gc252X2NsdXN0ZXJfZmlsZXNbW2ldXQogIHBhdGllbnRfaWQgPC0gc252X2NsdXN0ZXJfcGF0aWVudHNbW2ldXQogIHNudl9jbHVzdGVyIDwtIHJlYWRfc252X2NsdXN0ZXIoZiwgY2xvbmVfYnJhbmNoX2xlbmd0aF9maWxlLCBkYl9wYXRoKQogIHJldHVybihzbnZfY2x1c3RlcikKfSkpCgpzbnZfY2x1c3RlciA8LSBwbHlyOjpqb2luKHNudl9jbHVzdGVyLCBicmFuY2hfbGVuZ3RocykKYGBgCgpgYGB7cn0Kc252X2NsdXN0ZXJfZmlsdGVyZWQgPC0gc3Vic2V0KHNudl9jbHVzdGVyLCAhaXMubmEobGFiZWwpKQoKc2lnX3Jlc3VsdHNfc252X2NsdXN0ZXIgPC0gcGx5cjo6am9pbihzaWdfcmVzdWx0c19zbnYsIHNudl9jbHVzdGVyX2ZpbHRlcmVkKQpgYGAKCmBgYHtyfQpicmFuY2hfcHJvcHNfc252IDwtIHVuaXF1ZShzdWJzZXQoc2lnX3Jlc3VsdHNfc252X2NsdXN0ZXIsIHNlbGVjdD1jKCJwYXRpZW50X2lkIiwgImxhYmVsIiwgImNocm9tIiwgImNvb3JkIiwgInJlZiIsICJhbHQiLCBzaWduYXR1cmVfbGFiZWxzW3NpZ25hdHVyZV9sYWJlbHMgJWluJSBjb2xuYW1lcyhzaWdfcmVzdWx0c19zbnZfY2x1c3RlcildKSkpICU+JSBzdW1tYXJpemVfc2lnbmF0dXJlX3Byb3BvcnRpb25zKGJ5PWMoInBhdGllbnRfaWQiLCAibGFiZWwiKSwgc2lnbmF0dXJlX2xhYmVscywgcmVwb3J0X2NvdW50ID0gVFJVRSkKYGBgCgpgYGB7cn0KYnJhbmNoX3Byb3BzX3Nudl9tZWx0ZWQgPC0gbWVsdChicmFuY2hfcHJvcHNfc252LCBpZC52YXJzID0gYygicGF0aWVudF9pZCIsICJsYWJlbCIsICJuIiksIG1lYXN1cmUudmFycyA9IGludGVyc2VjdChzaWduYXR1cmVfbGFiZWxzLCBjb2xuYW1lcyhicmFuY2hfcHJvcHNfc252KSksIHZhcmlhYmxlLm5hbWUgPSAic2lnbmF0dXJlIiwgdmFsdWUubmFtZSA9ICJwcm9wb3J0aW9uIikKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9MjV9CmdncGxvdChicmFuY2hfcHJvcHNfc252X21lbHRlZCwgYWVzKHg9bGFiZWwsIHk9cHJvcG9ydGlvbikpICsgZ2VvbV9iYXIoYWVzKGZpbGw9c2lnbmF0dXJlKSwgc3RhdCA9ICJpZGVudGl0eSIpICsgZmFjZXRfd3JhcCh+IHBhdGllbnRfaWQsIG5jb2w9MSwgc2NhbGVzID0gImZyZWVfeCIpICsgdGhlbWVfYncoKSArIHRoZW1lX1B1YmxpY2F0aW9uKCkgKyBnZW9tX3RleHQoZGF0YT11bmlxdWUoc3Vic2V0KGJyYW5jaF9wcm9wc19zbnZfbWVsdGVkLCBzZWxlY3Q9YygicGF0aWVudF9pZCIsICJsYWJlbCIsICJuIikpKSwgYWVzKHg9bGFiZWwsIHk9MSwgbGFiZWw9biksdmp1c3Q9LS4yLHN0YXQ9ImlkZW50aXR5IikgKyB5bGltKGMoMCwgMS4yKSkKYGBgCgpXaGF0IGlmIHdlIHVzZSBNQVAgYXNzaWdubWVudHM/IChUaGlzIHdvdWxkIGFsbG93IHVzIHRvIGFwcGx5IGNoaS1zcXVhcmUgdGVzdHMuKSAKClRPRE86IEZpZ3VyZSBvdXQgaG93IHRvIGFwcGx5IHRlc3RzIGJldHdlZW4gayA+IDIgZ3JvdXBzIG9mIHByb3BvcnRpb25zLi4uIGluIG90aGVyIHdvcmRzLCBhIHRlc3Qgb2YgaG9tb2dlbmVpdHkuIAoKYGBge3J9CmlkX3ZhcnMgPC0gY29sbmFtZXMoc2lnX3Jlc3VsdHNfc252X2NsdXN0ZXIpWyFjb2xuYW1lcyhzaWdfcmVzdWx0c19zbnZfY2x1c3RlcikgJWluJSBzaWduYXR1cmVfbGFiZWxzXQoKc2lnX3Jlc3VsdHNfc252X2NsdXN0ZXJfbWVsdGVkIDwtIG1lbHQoc2lnX3Jlc3VsdHNfc252X2NsdXN0ZXIsIGlkLnZhcnMgPSBpZF92YXJzLCBtZWFzdXJlLnZhcnMgPSBpbnRlcnNlY3Qoc2lnbmF0dXJlX2xhYmVscywgY29sbmFtZXMoc2lnX3Jlc3VsdHNfc252X2NsdXN0ZXIpKSwgdmFyaWFibGUubmFtZSA9ICJzaWduYXR1cmUiLCB2YWx1ZS5uYW1lID0gInByb3BvcnRpb24iKQptYXhlcyA8LSBzaWdfcmVzdWx0c19zbnZfY2x1c3Rlcl9tZWx0ZWQgJT4lIGdyb3VwX2J5XyguZG90cyA9IGlkX3ZhcnMpICU+JSBzdW1tYXJpc2UobWF4cHJvcD1tYXgocHJvcG9ydGlvbikpCnNpZ19yZXN1bHRzX3Nudl9jbHVzdGVyX21lbHRlZCA8LSBwbHlyOjpqb2luKHNpZ19yZXN1bHRzX3Nudl9jbHVzdGVyX21lbHRlZCxtYXhlcykKc2lnX3Jlc3VsdHNfc252X2NsdXN0ZXJfbWVsdGVkJHByb3BvcnRpb25fbWFwIDwtIGlmZWxzZShzaWdfcmVzdWx0c19zbnZfY2x1c3Rlcl9tZWx0ZWQkcHJvcG9ydGlvbiA9PSBzaWdfcmVzdWx0c19zbnZfY2x1c3Rlcl9tZWx0ZWQkbWF4cHJvcCwgMSwgMCkKCnNpZ19yZXN1bHRzX3Nudl9jbHVzdGVyX2Nhc3RlZCA8LSBkY2FzdChzaWdfcmVzdWx0c19zbnZfY2x1c3Rlcl9tZWx0ZWQsIGZvcm11bGEgPSBwYXN0ZTAocGFzdGUoaWRfdmFycywgY29sbGFwc2U9IisiKSwgIn4gc2lnbmF0dXJlIiksIHZhbHVlLnZhciA9ICJwcm9wb3J0aW9uX21hcCIpCgpicmFuY2hfcHJvcHNfc252X21hcCA8LSB1bmlxdWUoc3Vic2V0KHNpZ19yZXN1bHRzX3Nudl9jbHVzdGVyX2Nhc3RlZCwgc2VsZWN0PWMoInBhdGllbnRfaWQiLCAibGFiZWwiLCAiY2hyb20iLCAiY29vcmQiLCAicmVmIiwgImFsdCIsIHNpZ25hdHVyZV9sYWJlbHNbc2lnbmF0dXJlX2xhYmVscyAlaW4lIGNvbG5hbWVzKHNpZ19yZXN1bHRzX3Nudl9jbHVzdGVyX2Nhc3RlZCldKSkpICU+JSBzdW1tYXJpemVfc2lnbmF0dXJlX3Byb3BvcnRpb25zKGJ5PWMoInBhdGllbnRfaWQiLCAibGFiZWwiKSwgc2lnbmF0dXJlX2xhYmVscywgcmVwb3J0X2NvdW50ID0gVFJVRSkKCmJyYW5jaF9wcm9wc19zbnZfbWFwX21lbHRlZCA8LSBtZWx0KGJyYW5jaF9wcm9wc19zbnZfbWFwLCBpZC52YXJzID0gYygicGF0aWVudF9pZCIsICJsYWJlbCIsICJuIiksIG1lYXN1cmUudmFycyA9IGludGVyc2VjdChzaWduYXR1cmVfbGFiZWxzLCBjb2xuYW1lcyhicmFuY2hfcHJvcHNfc252X21hcCkpLCB2YXJpYWJsZS5uYW1lID0gInNpZ25hdHVyZSIsIHZhbHVlLm5hbWUgPSAicHJvcG9ydGlvbiIpCmBgYAoKCmBgYHtyLCBmaWcud2lkdGg9NywgZmlnLmhlaWdodD0yNX0KZ2dwbG90KGJyYW5jaF9wcm9wc19zbnZfbWFwX21lbHRlZCwgYWVzKHg9bGFiZWwsIHk9cHJvcG9ydGlvbikpICsgZ2VvbV9iYXIoYWVzKGZpbGw9c2lnbmF0dXJlKSwgc3RhdCA9ICJpZGVudGl0eSIpICsgZmFjZXRfd3JhcCh+IHBhdGllbnRfaWQsIG5jb2w9MSwgc2NhbGVzID0gImZyZWVfeCIpICsgdGhlbWVfYncoKSArIHRoZW1lX1B1YmxpY2F0aW9uKCkgKyBnZW9tX3RleHQoZGF0YT11bmlxdWUoc3Vic2V0KGJyYW5jaF9wcm9wc19zbnZfbWFwX21lbHRlZCwgc2VsZWN0PWMoInBhdGllbnRfaWQiLCAibGFiZWwiLCAibiIpKSksIGFlcyh4PWxhYmVsLCB5PTEsIGxhYmVsPW4pLHZqdXN0PS0uMixzdGF0PSJpZGVudGl0eSIpICsgeWxpbShjKDAsIDEuMikpCmBgYAoKIyMjIEFkanVzdGVkIGNsb25lIHRyZWVzCgpgYGB7cn0KdHJlZXNfYWdlIDwtIHRyZWVzCmFnZV9zaWduYXR1cmUgPC0gIlNOVi01IgoKYnJhbmNoX3Byb3BzX2RhdCA8LSBicmFuY2hfcHJvcHNfc252X21hcF9tZWx0ZWQKCnRyZWVfb2JqZWN0cyA8LSBsYXBwbHkoc2VxX2Fsb25nKHRyZWVzX2FnZSksIGZ1bmN0aW9uKGkpIHsKICB0cmVlIDwtIHRyZWVzX2FnZVtbaV1dCiAgdHJlZV9vbGQgPC0gdHJlZQogIHBhdGllbnQgPC0gbmFtZXModHJlZXNfYWdlKVtpXQogIGJyYW5jaF9wcm9wc19kYXRfc3ViIDwtIHN1YnNldChicmFuY2hfcHJvcHNfZGF0LCBwYXRpZW50X2lkID09IGFzLm51bWVyaWMocGF0aWVudCkgJiBzaWduYXR1cmUgPT0gYWdlX3NpZ25hdHVyZSkKICBicmFuY2hfcHJvcHNfZGF0X3N1YiRsZW5ndGggPC0gd2l0aChicmFuY2hfcHJvcHNfZGF0X3N1YiwgcHJvcG9ydGlvbipuKQogIAogIGVkZ2VfbGVuZ3RocyA8LSB0cmVlQGVkZ2UubGVuZ3RoCiAgaWR4IDwtIHdoaWNoKGVkZ2VfbGVuZ3RocyA9PSAwKQogIHRvX2xhYmVscyA8LSB0cmVlQGxhYmVsW3N0cl9leHRyYWN0KG5hbWVzKGVkZ2VfbGVuZ3RocyksICJbMC05XSskIildCiAgbGVuZ3RocyA8LSBkZl9hc19tYXAoYnJhbmNoX3Byb3BzX2RhdF9zdWIsIHVubmFtZSh0b19sYWJlbHMpLCBmcm9tID0gImxhYmVsIiwgdG89Imxlbmd0aCIpCiAgaWYgKGxlbmd0aChpZHgpID4gMCkgewogICAgbGVuZ3Roc1tpZHhdIDwtIDAKICB9CiAgdHJlZUBlZGdlLmxlbmd0aCA8LSBsZW5ndGhzCiAgbmFtZXModHJlZUBlZGdlLmxlbmd0aCkgPC0gbmFtZXMoZWRnZV9sZW5ndGhzKQogIAogIHJldHVybihsaXN0KGFsbD10cmVlX29sZCwgYWdlPXRyZWUpKQp9KQpuYW1lcyh0cmVlX29iamVjdHMpIDwtIG5hbWVzKHRyZWVzX2FnZSkKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTIwLCBmaWcuaGVpZ2h0PTIwfQp0bXAgPC0gdW5saXN0KHRyZWVfb2JqZWN0cykKI2lnbm9yZSA8LSBsYXBwbHkoc2VxX2Fsb25nKHRtcCksIGZ1bmN0aW9uKGkpIHsKIyAgcHJpbnQocGxvdCh0bXBbW2ldXSwgc2hvdy5ub2RlLmxhYmVsID0gVFJVRSwgbWFpbiA9IG5hbWVzKHRtcClbaV0pKQojfSkKYGBgCgpgYGB7cn0KZmluZF9hbmNlc3RvcnMgPC0gZnVuY3Rpb24odHJlZSkgewogIHggPC0gcGh5bG9iYXNlOjo6LnBoeWxvNFRvRGF0YUZyYW1lKHRyZWUpCiAgcm9vdCA8LSBzdWJzZXQoeCwgbm9kZS50eXBlID09ICJyb290IikkbGFiZWwKICByb290X251bWJlciA8LSBuYW1lcyh3aGljaCh0cmVlQGxhYmVsID09IHJvb3QpKQogIGRpcmVjdF9kZXNjZW5kYW50cyA8LSBzdWJzZXQoeCwgYW5jZXN0b3IgPT0gYXMubnVtZXJpYyhyb290X251bWJlcikpJGxhYmVsCiAgaWYgKGxlbmd0aChkaXJlY3RfZGVzY2VuZGFudHMpID09IDEpIHsKICAgIHJldHVybihjKHJvb3QsIGRpcmVjdF9kZXNjZW5kYW50cykpCiAgfSBlbHNlIHsKICAgIHJldHVybihyb290KQogIH0KfQpgYGAKCmBgYHtyfQpwYXRpZW50cyA8LSB1bmlxdWUoYnJhbmNoX3Byb3BzX2RhdCRwYXRpZW50X2lkKQpyb290X2RhdGEgPC0gcmJpbmQuZmlsbChsYXBwbHkocGF0aWVudHMsIGZ1bmN0aW9uKHBhdCkgewogIHRyZWUgPC0gdHJlZXNbW2FzLmNoYXJhY3RlcihwYXQpXV0KICBhbmNlc3RvcnMgPC0gZmluZF9hbmNlc3RvcnModHJlZSkKICByYmluZC5maWxsKGxhcHBseShhbmNlc3RvcnMsIGZ1bmN0aW9uKHgpIHsKICAgIGRhdGEuZnJhbWUobGFiZWw9eCwgcGF0aWVudF9pZD1wYXQpCiAgfSkpCn0pKQpyb290X2RhdGEkbm9kZV90eXBlIDwtICJyb290IgpgYGAKCmBgYHtyfQpicmFuY2hfZGF0YV9hbm5vdGF0ZWQgPC0gcGx5cjo6am9pbihicmFuY2hfcHJvcHNfZGF0LCByb290X2RhdGEpCmJyYW5jaF9kYXRhX2Fubm90YXRlZCRub2RlX3R5cGVbaXMubmEoYnJhbmNoX2RhdGFfYW5ub3RhdGVkJG5vZGVfdHlwZSldIDwtICJkZXNjZW5kYW50IgoKcm9vdF9wcm9wb3J0aW9ucyA8LSBicmFuY2hfZGF0YV9hbm5vdGF0ZWQgJT4lIHN1YnNldChub2RlX3R5cGUgPT0gInJvb3QiKSAlPiUgZ3JvdXBfYnkocGF0aWVudF9pZCwgc2lnbmF0dXJlKSAlPiUgc3VtbWFyaXNlKHByb3BvcnRpb249d2VpZ2h0ZWQubWVhbihwcm9wb3J0aW9uLCB3ID0gbikpICU+JSBwbHlyOjpyZW5hbWUoYygncHJvcG9ydGlvbic9J3Jvb3RfcHJvcG9ydGlvbicpKQoKYnJhbmNoX2RhdGFfYW5ub3RhdGVkIDwtIHBseXI6OmpvaW4oYnJhbmNoX2RhdGFfYW5ub3RhdGVkLCByb290X3Byb3BvcnRpb25zKQpicmFuY2hfZGF0YV9hbm5vdGF0ZWQkcHJvcG9ydGlvbl9kaWZmIDwtIHdpdGgoYnJhbmNoX2RhdGFfYW5ub3RhdGVkLCBwcm9wb3J0aW9uIC0gcm9vdF9wcm9wb3J0aW9uKQpgYGAKCmBgYHtyfQpnZ3Bsb3QoYnJhbmNoX2RhdGFfYW5ub3RhdGVkICU+JSBzdWJzZXQobm9kZV90eXBlID09ICJkZXNjZW5kYW50IiAmIG4gPiA0MCksIGFlcyh4PXByb3BvcnRpb25fZGlmZiwgZmlsbD1zaWduYXR1cmUpKSArIGdlb21faGlzdG9ncmFtKGJpbndpZHRoPTAuMDIsIGFscGhhPTAuNCwgcG9zaXRpb249J2lkZW50aXR5JykgKyB0aGVtZV9idygpICsgdGhlbWVfUHVibGljYXRpb24oKQpgYGA=